コーディング用のボイラープレートを作成する(Vite, ESLint, Prettier, stylelint, Sass, Docker)

社内でコーディングを依頼することになり、ある程度ルールがある中でコードを書いてほしいと思い今回コーディング用のボイラープレートを作成しました。

使ってもらってより良い設定があればこの記事とGitHubのコードも更新していこうかと思っています

完成品

github.com

ライブラリ等

ja.vitejs.dev

eslint.org

prettier.io

stylelint.io

sass-lang.com

Vite プロジェクトの作成

$ npm create vite@latest

✔ Project name: … coding_boilerplate
✔ Select a framework: › vanilla
✔ Select a variant: › vanilla

$ cd coding_boilerplate
  • framework は vanilla を選択します。

開発場所になるディレクトリの作成

1. vite.config.js ファイルを新規に作成し以下のように入力します。

// vite.config.js

import { defineConfig } from 'vite'

export default defineConfig({
  root: 'src',
  build: {
    outDir: '../dist'
  }
})

2. 次に src ディレクトリを作成し index.html, main.js, style.css, favicon.svg を 作成したディレクトリに移動させます。

$ mkdir src
$ mv index.html main.js style.css favicon.svg src/.

3. サーバーを起動し localhost:3000 にアクセスし表示できるか確認します。

$ npm run dev

localhost:3000

Sass のインストール

$ npm install --save-dev sass

ファイル名を変更する

$ mv src/style.css src/style.scss
// src/main.js

import './style.scss'

Prettier のインストール

$ npm install --save-dev prettier
// .prettierrc

{
  "printWidth": 120,
  "singleQuote": true,
  "semi": false,
  "tabWidth": 2,
  "trailingComma": "none"
}
.prettierignore

dist/
node_modules/

ESLint のインストール

必要なライブラリをインストールします。

$ npm install --save-dev eslint eslint-config-prettier
  • eslint-config-prettier を導入すると Prettierと競合する ESLint のルールを無効化することができます。

ESLint のセットアップをします。

$ npx eslint --init

✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · guide
✔ Which style guide do you want to follow? · standard
✔ What format do you want your config file to be in? · JSON
✔ Would you like to install them now? · Yes
✔ Which package manager do you want to use? · npm

生成されたファイルが以下になります。

// .eslintrc.json

{
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "standard"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": "latest",
        "sourceType": "module"
    },
    "plugins": [
        "@typescript-eslint"
    ],
    "rules": {
    }
}

Stylelint のインストール

必要なライブラリをインストールします。

$ npm install --save-dev stylelint stylelint-config-standard-scss stylelint-config-prettier-scss stylelint-config-recess-order
  • stylelint-config-prettier-scss を導入すると Prettierと競合する Stylelint のルールを無効化することができます。

Stylelintのセットアップをします。

// .stylelintrc.json

{
  "plugins": [],
  "extends": [
    "stylelint-config-standard-scss",
    "stylelint-config-prettier-scss",
    "stylelint-config-recess-order"
  ],
  "rules": {
    "color-no-invalid-hex": true,
    "comment-no-empty": true
  },
  "ignoreFiles": ["./node_modules/**", "./dist/**"]
}

スクリプトの追加

// package.json

{
  ...
  "scripts": {
    ...
    "lint": "npm run lint:prettier && npm run lint:eslint && npm run lint:style ",
    "lint:prettier": "prettier --check --loglevel=warn '**/*.{js,ts,scss}'",
    "lint:eslint": "eslint '**/*.{js,ts}'",
    "lint:style": "stylelint '**/*.{css,scss}'",
    "format": "npm run format:prettier && npm run format:eslint && npm run format:style",
    "format:prettier": "prettier --check --write --loglevel=warn '**/*.{js,ts,scss}'",
    "format:eslint": "eslint --fix '**/*.{js,ts}'",
    "format:style": "stylelint --fix '**/*.{css,scss}'"
  },
  ...

テンプレートの導入

コーディングする際にヘッダーやフッターなどパーシャル(サイトの繰り返し部分)と呼ばれる部品を組み合わせることができます。

必要なライブラリをインストールします。

$ npm install --save-dev vite-plugin-handlebars

設定ファイルを変更します。

$ vite.config.js

import { resolve } from 'path' // 追加
import { defineConfig } from 'vite'
import handlebars from 'vite-plugin-handlebars' // 追加

export default defineConfig({
  root: 'src',
  build: {
    outDir: '../dist'
  },
  // 追加
  plugins: [
    handlebars({
      partialDirectory: resolve(__dirname, 'src/partials'),
      context: {
        title: 'Coding boilerplate'
      }
    })
  ]
})
$ mkdir src/partials
// src/partials/header.html

<header>
  <a href="/">Home</a>
</header>
// src/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{ title }}</title> <!-- 修正 -->
  </head>
  <body>
    {{> header }} <!-- 追加 -->
    <div id="app"></div>
    <script type="module" src="/main.js"></script>
  </body>
</html>

これでレイアウトを実現することができます。

Docker 用の設定追加

# Dockerfile

FROM node:16.14-buster-slim

WORKDIR /app

EXPOSE 3000
# docker-compose.yml

version: '3.8'

services:
  node:
    build:
      context: .
    tty: true
    command: "npm run dev"
    ports:
      - "${NODE_PORT:-8080}:3000"
    volumes:
      - ".:/app"
# Makefile

RUN := run --rm
DOCKER_COMPOSE_RUN := docker-compose $(RUN)

init:
    @make build
    @make npm_install

build:
    docker-compose build

rebuild:
    docker-compose build --force-rm --no-cache

up:
    docker-compose up

upd:
    docker-compose up -d

down:
    docker-compose down

attach:
    docker attach (docker-compose ps -q app)

npm_install:
    ${DOCKER_COMPOSE_RUN} node npm install

lint:
    ${DOCKER_COMPOSE_RUN} node npm run lint

format:
    ${DOCKER_COMPOSE_RUN} node npm run format

app_build:
    ${DOCKER_COMPOSE_RUN} node npm run build

down.all:
    if [ -n "`docker ps -q`" ]; then docker kill `docker ps -q`; fi
        docker container prune -f
// vite.config.js

...
export default defineConfig({
  root: 'src',
  // 追加
  server: {
    host: true
  },
  ...
$ make init
$ make upd

http://localhost:8080 にアクセスする

localhost:8000

VScode の設定

VSCode の設定も載せておきます

// .vscode/settings.json

{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "css.validate": false,
  "less.validate": false,
  "scss.validate": false,
  "editor.formatOnSave": false,
  "editor.codeActionsOnSave": {
    "source.fixAll.stylelint": true
  },
  "stylelint.validate": ["css", "scss"],
  "stylelint.configFile": "./.stylelintrc.json"
}

試しに作ってみて改良の余地ありありなので改善点コメントなどで教えてもらえれば嬉しいです。

mogenerator コマンドで 「skipping entity User (NSManagedObject) because it doesn&#39;t use a custom subclass.」が出た場合の対処法

問題

mogenerator コマンドを叩いたら以下のようなエラーが発生。

$ mogenerator -m swift-ios-example/swift-ios-example.xcdatamodeld/swift-ios-example.xcdatamodel/ -O Models/ --template-var arc=true

skipping entity User (NSManagedObject) because it doesn't use a custom subclass.
skipping entity User (NSManagedObject) because it doesn't use a custom subclass.
0 machine files and 0 human files generated.

原因

クラス名を指定していなかった。

解決方法

クラス名を指定する。

f:id:kzy52:20150526093607p:plain

$ mogenerator -m swift-ios-example/swift-ios-example.xcdatamodeld/swift-ios-example.xcdatamodel/ -O Models/ --template-var arc=true --swift

1 machine files and 1 human files generated.

mogenerator コマンドで 「skipping entity User (NSManagedObject) because it doesn&#39;t use a custom subclass.」が出た場合の対処法

問題

mogenerator コマンドを叩いたら以下のようなエラーが発生。

$ mogenerator -m swift-ios-example/swift-ios-example.xcdatamodeld/swift-ios-example.xcdatamodel/ -O Models/ --template-var arc=true

skipping entity User (NSManagedObject) because it doesn't use a custom subclass.
skipping entity User (NSManagedObject) because it doesn't use a custom subclass.
0 machine files and 0 human files generated.

原因

クラス名を指定していなかった。

解決方法

クラス名を指定する。

f:id:kzy52:20150526093607p:plain

$ mogenerator -m swift-ios-example/swift-ios-example.xcdatamodeld/swift-ios-example.xcdatamodel/ -O Models/ --template-var arc=true --swift

1 machine files and 1 human files generated.

CentOS に Errbit をインストールする

環境

CentOS 6.6

必要なライブラリーをインストールする

$ sudo yum install -y git nginx nodejs

MongoDB のインストールと起動

# /etc/yum.repos.d/mongodb-org.repo

[mongodb-org]
name=MongoDB Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/
gpgcheck=0
enabled=1
$ sudo yum install -y mongodb-org
$ sudo service mongod start
$ sudo chkconfig mongod on

Nginx の設定

# /etc/nginx/conf.d/errbit.conf

upstream errbit {
  server unix:/var/www/vhost/errbit/tmp/sockets/unicorn.sock;
}

server {
  listen *:80;
  server_name errbit.example.com;
  server_tokens off;
  root /var/www/vhost/errbit/public;

  location / {
    try_files $uri $uri/index.html $uri.html @errbit;
  }

  location ~ ^/(assets)/ {
    root /var/www/vhost/errbit/public;
    access_log off;
  }

  location @errbit {
    proxy_set_header    Host                $http_host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_pass http://errbit;
  }
}

Nginx の起動

$ sudo service nginx start

Errbit のインストール

$ sudo mkdir -p /var/www/vhost
$ cd /var/www/vhost
$ sudo git clone https://github.com/errbit/errbit.git
$ sudo chown op:op -R errbit/
$ cd errbit/
$ bundle install --path vendor/bundle
$ bundle exec rake errbit:bootstrap
$ bundle exec rake assets:precompile RAILS_ENV=production
$ cp config/unicorn.default.rb config/unicorn.rb

unicorn の設定

# config/unicorn.rb

worker_processes 3
timeout 30
preload_app true

app_path = '/var/www/vhost/errbit'
working_directory = app_path

listen  File.expand_path('tmp/sockets/unicorn.sock', app_path)
pid File.expand_path('tmp/pids/unicorn.pid', app_path)
stderr_path File.expand_path('log/unicorn.stderr.log', app_path)
stdout_path File.expand_path('log/unicorn.stdout.log', app_path)

before_fork do |server, worker|
  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end

Errbit の起動

$ bundle exec unicorn -c config/unicorn.rb -E production -D

f:id:kzy52:20150514081049p:plain

参考ページ

errbit/errbit · GitHub

CentOS に Ruby をインストールする

環境

CentOS 6.6

rbenv

Ruby のバージョン管理ツール。

ruby-build

Rubyの各バージョンのインストールツール。
rbenv install コマンドが使用できるようになる。

rbenv-default-gems

Ruby をインストールするときに指定した gem を自動でインストールしてくれるツール。

依存パッケージをインストールする

$ sudo yum -y install openssl-devel readline-devel zlib-devel libcurl-devel

rbenv のインストール

$ sudo su -

# cd /usr/local
# git clone git://github.com/sstephenson/rbenv.git rbenv
# mkdir rbenv/shims rbenv/versions rbenv/plugins
# groupadd rbenv
# chgrp -R rbenv rbenv
# chmod -R g+rwxXs rbenv

ruby-build のインストール

# cd /usr/local/rbenv/plugins

# git clone git://github.com/sstephenson/ruby-build.git ruby-build
# chgrp -R rbenv ruby-build
# chmod -R g+rwxs ruby-build

rbenv-default-gems のインストール

# git clone git://github.com/sstephenson/rbenv-default-gems.git rbenv-default-gems
# chgrp -R rbenv rbenv-default-gems
# chmod -R g+rwxs rbenv-default-gems

環境変数の設定

# /etc/profile.d/rbenv.sh

export RBENV_ROOT="/usr/local/rbenv"
export PATH="$RBENV_ROOT/bin:$PATH"
eval "$(rbenv init -)"

デフォルトでインストールする gem の指定

# /usr/local/rbenv/default-gems

bundler
rbenv-rehash

設定の反映

# source /etc/profile.d/rbenv.sh

インストール可能な Ruby のバージョン確認

$ rbenv install -l

1.8.6-p383
1.8.6-p420
...
2.2.1
2.2.2
2.3.0-dev
jruby-1.5.6
...

Ruby のインストール

ここでは 2.2.2 をインストールし、グローバルとして設定する。

# rbenv install 2.2.2
# rbenv global 2.2.2
# ruby -v
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]

Jenkins で 静的コードチェックを自動化する MetricFu, RuboCop, Brakeman

ソースコードの品質向上のため静的コード解析ツールである MetricFu , RuboCop , Brakeman のドキュメント出力を Jenkins で自動化する方法をまとめました。
これらを導入することにより人力コードレビューの負担を少しでも減らすことができるかなと思います。

MetricFu

metricfu/metric_fu · GitHub

コードメトリクスを測定。 ソースコードの重複チェック、処理の複雑度などを出力してくれる。
Reek, Flay, Chrn, Stats, Flog, Cane, Roodi, Saikuro, Rails Best Practices, Hotspots

RuboCop

bbatsov/rubocop · GitHub

静的コード解析ツール。
Ruby Style Guide に沿っているかチェックしてくれる。

Brakeman

presidentbeef/brakeman · GitHub

セキュリティチェック。
脆弱性の含まれたライブラリを使っていたり、セキュリティ的にまずい実装をしていると警告してくれる。

MetricFu

gem のインストール

# Gemfile

gem 'metric_fu'

Jenkins の設定

プラグインのインストール

[Jenkinsの管理] > [プラグインの管理] から HTML Publisher Plugin プラグインをインストールする。

HTML Publisher Plugin プラグイン の設定

[ビルド後の処理の追加] > [Publish HTML reports] を選択し以下のように入力する。

f:id:kzy52:20150126224437p:plain

HTML directory to archive: tmp/metric_fu/output
Index page[s]: index.html
Report title: metric-fu

シェルスクリプトの設定

bundle exec metric_fu

f:id:kzy52:20150128221618p:plain

f:id:kzy52:20150128221447p:plain

RuboCop

gem のインストール

# Gemfile

gem 'rubocop', require: false
gem 'rubocop-checkstyle_formatter', require: false

Jenkins の設定

プラグインのインストール

[Jenkinsの管理] > [プラグインの管理] から Violations プラグインをインストールする

Violations プラグインの設定

[ビルド後の処理の追加] > [Report Violations] を選択し以下のように入力する。

f:id:kzy52:20150126225100p:plain

checkstyle: tmp/checkstyle.xml

シェルスクリプトの設定

bundle exec rubocop --require rubocop/formatter/checkstyle_formatter --format RuboCop::Formatter::CheckstyleFormatter --no-color --rails --out tmp/checkstyle.xml

f:id:kzy52:20150128222030p:plain

f:id:kzy52:20150128222104p:plain

Brakeman

gem のインストール

# Gemfile

gem 'brakeman'

Jenkins の設定

プラグインのインストール

[Jenkinsの管理] > [プラグインの管理] から Brakeman Plugin プラグインをインストールする。

Brakeman Plugin プラグインの設定

[ビルド後の処理の追加] > [Publish Brakeman warnings] を選択し以下のように入力する。

f:id:kzy52:20150126225556p:plain

Brakeman Output File: tmp/brakeman-output.tabs

シェルスクリプトの設定

bundle exec brakeman -o tmp/brakeman-output.tabs

f:id:kzy52:20150129204124p:plain

f:id:kzy52:20150129204137p:plain

f:id:kzy52:20150129204314p:plain

気に入ったものがあれば是非導入してみてください。

iOS - Swift で WebViewアプリを作ってみた

Swift で WebView を使ってWebページを表示するだけのアプリを作ってみます。
初めて実装する人でもわかるよう、細かくコメント入れています。
是非、お試しください。

テンプレートは Single View Application にします。

Webページを表示する

UIWebViewクラスを使って WebView を実装していきます。
WebView を実装するには他にも WKWebView というクラスがあります。

class ViewController: UIViewController {
    
    var webView: UIWebView?
    
    var targetURL = "http://www.yahoo.co.jp/"

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // WebViewを生成する
        self.webView = self.createWebView()
        
        // サブビューを追加する
        self.view.addSubview(self.webView!)
        
        // リクエストを生成する
        var url = NSURL(string: targetURL)
        var request = NSURLRequest(URL: url!)
        
        // 指定したページを読み込む
        self.webView?.loadRequest(request)
    }
    
    // WebView を生成する
    func createWebView() -> UIWebView {
        // UIWebViewのインスタンスを生成
        let _webView = UIWebView()
        
        // 全画面表示にする
        _webView.frame = self.view.bounds
        
        return _webView
    }

f:id:kzy52:20150218211310p:plain

ステータスバーとビューが被らないようにする

ステータスバーと追加したビューが被ってしまっているのでステータスバーの高さに合わせてビューのサイズを変更します。

class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // 横幅、高さ、ステータスバーの高さを取得する
        let width: CGFloat! = self.view.bounds.width
        let height: CGFloat! = self.view.bounds.height
        let statusBarHeight: CGFloat! = UIApplication.sharedApplication().statusBarFrame.height
        
        // WebViewを生成する
        self.webView = self.createWebView(frame: CGRectMake(0, statusBarHeight, width, height - statusBarHeight))
        ...
    }
    
    // WebView を生成する
    func createWebView(#frame: CGRect) -> UIWebView {
        // UIWebViewのインスタンスを生成
        let _webView = UIWebView()
        
        // 画面サイズを設定する
        _webView.frame = frame
        
        return _webView
    }
    ...
}

UIApplication.sharedApplication().statusBarFrame.height

これでステータスバーの高さを取得できます。

CGRectMake

対象オブジェクトのサイズと位置を指定したい場合に使用します。

CGRect rect = CGRectMake(位置x, 位置y, 幅, 高さ)

このように生成します。

f:id:kzy52:20150218212526p:plain

ランドスケープモード(横)で表示が崩れないようにする

class ViewController: UIViewController {
    ...

    func createWebView(#frame: CGRect) -> UIWebView {
        ...
        _webView.frame = frame
        
        // ビューサイズの自動調整
        _webView.autoresizingMask = UIViewAutoresizing.FlexibleRightMargin |
            UIViewAutoresizing.FlexibleTopMargin |
            UIViewAutoresizing.FlexibleLeftMargin |
            UIViewAutoresizing.FlexibleBottomMargin |
            UIViewAutoresizing.FlexibleWidth |
            UIViewAutoresizing.FlexibleHeight
        
        return _webView
    }

    // ビューが再レイアウトされるときに呼び出される
    override func viewWillLayoutSubviews() {
        let statusBarHeight: CGFloat! = UIApplication.sharedApplication().statusBarFrame.height
        self.webView?.frame = CGRectMake(0, statusBarHeight, self.view.bounds.width, self.view.bounds.height)
    }
}

autoresizingMask

子要素の自動調整をしてくれます。
値は以下を指定することができます。

設定値 説明
UIViewAutoresizingFlexibleTopMargin 上を自動調整
UIViewAutoresizingFlexibleBottomMargin 下を自動調整
UIViewAutoresizingFlexibleRightMargin 右を自動調整
UIViewAutoresizingFlexibleLeftMargin 左を自動調整
UIViewAutoresizingFlexibleWidth 幅を自動調整
UIViewAutoresizingFlexibleHeight 高さを自動調整
UIViewAutoresizingNone 自動調整なし

viewWillLayoutSubviews()

ビューが再レイアウトされるときに呼び出されます。
ランドスケープモードだとステータスバーの部分が空いてしまうので再度ビューのサイズを設定しています。

f:id:kzy52:20150218222531p:plain

インジケータを表示する

class ViewController: UIViewController, UIWebViewDelegate {
    ...
    override func viewDidLoad() {
        ...
        self.webView?.loadRequest(request)
        
        // インジケータを表示する
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    }
    
    // WebView を生成する
    func createWebView(#frame: CGRect) -> UIWebView {
        // UIWebViewのインスタンスを生成
        let _webView = UIWebView()
        
        // デリゲートを指定する
        _webView.delegate = self;
        ...
    }
    ...
    // WebView がコンテンツの読み込みを完了した後に呼ばれる
    func webViewDidFinishLoad(webView: UIWebView) {
        // インジケータを非表示にする
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
    }
    ...
}

UIWebViewDelegate

ウェブコンテンツを読み込んだ時の処理を追加したいので UIWebViewDelegate プロトコルを追加します。
WebView がコンテンツの読み込みを開始した場合に呼ばれる webViewDidStartLoad と
完了した場合に呼ばれる webViewDidFinishLoad が使用できるようになります。

UIApplication.sharedApplication().networkActivityIndicatorVisible

ネットワークにアクセスしていることを示すインジケータの表示、非表示を設定することができます。

f:id:kzy52:20150218220334p:plain

戻る、進む、リフレッシュ、Safariで開くボタンを表示する

class ViewController: UIViewController, UIWebViewDelegate {
    ...    
    var toolBar: UIToolbar?
    var rewindButton = UIBarButtonItem()
    var fastForwardButton = UIBarButtonItem()
    var refreshButton = UIBarButtonItem()
    var openInSafari = UIBarButtonItem()

    override func viewDidLoad() {
        ...
        self.view.addSubview(self.webView!)
        
        // ツールバーを生成する
        self.toolBar = self.createToolBar(frame: CGRectMake(0, height-44, width, 40.0), position: CGPointMake(width/2, height-20.0))
        
        // サブビューを追加する
        self.view.addSubview(self.toolBar!)
        
        // リクエストを生成する
        ...
        
        // 前のページに戻れるかどうか
        self.rewindButton.enabled = self.webView!.canGoBack
        // 次のページに進めるかどうか
        self.fastForwardButton.enabled = self.webView!.canGoForward
        self.refreshButton.enabled = false
        self.openInSafari.enabled = false
    }
    ...
    
    // ツールバーを生成する
    func createToolBar(#frame: CGRect, position: CGPoint) -> UIToolbar {
        // UIWebViewのインスタンスを生成
        let _toolBar = UIToolbar()
        
        // ツールバーのサイズを決める.
        _toolBar.frame = frame
        
        // ツールバーの位置を決める.
        _toolBar.layer.position = position
        
        // 文字色を設定する
        _toolBar.tintColor = UIColor.blueColor()
        // 背景色を設定する
        _toolBar.backgroundColor = UIColor.whiteColor()
        
        // 各ボタンを生成する
        // UIBarButtonItem(style, デリゲートのターゲットを指定, ボタンが押されたときに呼ばれるメソッドを指定)
        let spacer: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil)
        self.rewindButton = UIBarButtonItem(barButtonSystemItem: .Rewind, target: self, action: "back:")
        self.fastForwardButton = UIBarButtonItem(barButtonSystemItem: .FastForward, target: self, action: "forward:")
        self.refreshButton = UIBarButtonItem(barButtonSystemItem: .Refresh, target: self, action: "refresh:")
        self.openInSafari = UIBarButtonItem(barButtonSystemItem: .Action, target: self, action: "safari:")
        
        // ボタンをツールバーに入れる.
        _toolBar.items = [rewindButton, fastForwardButton, refreshButton, spacer, openInSafari]
        
        return _toolBar
    }
    ...
    
    // WebViewがコンテンツの読み込みを開始した時に呼ばれる
    func webViewDidStartLoad(webView: UIWebView) {
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        
        self.rewindButton.enabled = self.webView!.canGoBack
        self.fastForwardButton.enabled = self.webView!.canGoForward
        self.refreshButton.enabled = true
        self.openInSafari.enabled = true
    }
    
    // WebView がコンテンツの読み込みを完了した後に呼ばれる
    func webViewDidFinishLoad(webView: UIWebView) {
        // インジケータを非表示にする
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
        
        self.rewindButton.enabled = self.webView!.canGoBack
        self.fastForwardButton.enabled = self.webView!.canGoForward
    }
    
    // 戻るボタンの処理
    @IBAction func back(AnyObject) {
        self.webView?.goBack()
    }
    
    // 進むボタンの処理
    @IBAction func forward(AnyObject) {
        self.webView?.goForward()
    }
    
    // 再読み込みボタンの処理
    @IBAction func refresh(AnyObject) {
        self.webView?.reload()
    }
    
    // safari で開く
    @IBAction func safari(AnyObject) {
        let url = self.webView?.request?.URL
        UIApplication.sharedApplication().openURL(url!)
    }
    ...
}

CGPointMake

対象オブジェクトの位置を指定したい場合に使用します。

CGPoint point = CGPointMake(位置x, 位置y);

このように生成します。

@IBAction

クリックイベントの定義には IBAction を使用します。

f:id:kzy52:20150218221905p:plain

最終的には以下のようになります。

import UIKit

class ViewController: UIViewController, UIWebViewDelegate {
    
    var webView: UIWebView?
    
    var targetURL = "http://www.yahoo.co.jp/"
    
    var toolBar: UIToolbar?
    var rewindButton = UIBarButtonItem()
    var fastForwardButton = UIBarButtonItem()
    var refreshButton = UIBarButtonItem()
    var openInSafari = UIBarButtonItem()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // 横幅、高さ、ステータスバーの高さを取得する
        let width: CGFloat! = self.view.bounds.width
        let height: CGFloat! = self.view.bounds.height
        let statusBarHeight: CGFloat! = UIApplication.sharedApplication().statusBarFrame.height
        
        // WebViewを生成する
        self.webView = self.createWebView(frame: CGRectMake(0, statusBarHeight, width, height - statusBarHeight))
        
        // サブビューを追加する
        self.view.addSubview(self.webView!)
        
        // ツールバーを生成する
        self.toolBar = self.createToolBar(frame: CGRectMake(0, height-44, width, 40.0), position: CGPointMake(width/2, height-20.0))
        
        // サブビューを追加する
        self.view.addSubview(self.toolBar!)
        
        // リクエストを生成する
        var url = NSURL(string: targetURL)
        var request = NSURLRequest(URL: url!)
        
        // 指定したページを読み込む
        self.webView?.loadRequest(request)
        
        // インジケータを表示する
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        
        // 前のページに戻れるかどうか
        self.rewindButton.enabled = self.webView!.canGoBack
        // 次のページに進めるかどうか
        self.fastForwardButton.enabled = self.webView!.canGoForward
        self.refreshButton.enabled = false
        self.openInSafari.enabled = false
    }
    
    // WebView を生成する
    func createWebView(#frame: CGRect) -> UIWebView {
        // UIWebViewのインスタンスを生成
        let _webView = UIWebView()
        
        // デリゲートを指定する
        _webView.delegate = self;
        
        // 画面サイズを設定する
        _webView.frame = frame
        
        // ビューサイズの自動調整
        _webView.autoresizingMask = UIViewAutoresizing.FlexibleRightMargin |
            UIViewAutoresizing.FlexibleTopMargin |
            UIViewAutoresizing.FlexibleLeftMargin |
            UIViewAutoresizing.FlexibleBottomMargin |
            UIViewAutoresizing.FlexibleWidth |
            UIViewAutoresizing.FlexibleHeight
        
        return _webView
    }
    
    // ツールバーを生成する
    func createToolBar(#frame: CGRect, position: CGPoint) -> UIToolbar {
        // UIWebViewのインスタンスを生成
        let _toolBar = UIToolbar()
        
        // ツールバーのサイズを決める.
        _toolBar.frame = frame
        
        // ツールバーの位置を決める.
        _toolBar.layer.position = position
        
        // 文字色を設定する
        _toolBar.tintColor = UIColor.blueColor()
        // 背景色を設定する
        _toolBar.backgroundColor = UIColor.whiteColor()
        
        // 各ボタンを生成する
        // UIBarButtonItem(style, デリゲートのターゲットを指定, ボタンが押されたときに呼ばれるメソッドを指定)
        let spacer: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil)
        self.rewindButton = UIBarButtonItem(barButtonSystemItem: .Rewind, target: self, action: "back:")
        self.fastForwardButton = UIBarButtonItem(barButtonSystemItem: .FastForward, target: self, action: "forward:")
        self.refreshButton = UIBarButtonItem(barButtonSystemItem: .Refresh, target: self, action: "refresh:")
        self.openInSafari = UIBarButtonItem(barButtonSystemItem: .Action, target: self, action: "safari:")
        
        // ボタンをツールバーに入れる.
        _toolBar.items = [rewindButton, fastForwardButton, refreshButton, spacer, openInSafari]
        
        return _toolBar
    }
    
    // ビューが再レイアウトされるときに呼び出される
    override func viewWillLayoutSubviews() {
        let statusBarHeight: CGFloat! = UIApplication.sharedApplication().statusBarFrame.height
        self.webView?.frame = CGRectMake(0, statusBarHeight, self.view.bounds.width, self.view.bounds.height)
    }
    
    // WebViewがコンテンツの読み込みを開始した時に呼ばれる
    func webViewDidStartLoad(webView: UIWebView) {
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        
        self.rewindButton.enabled = self.webView!.canGoBack
        self.fastForwardButton.enabled = self.webView!.canGoForward
        self.refreshButton.enabled = true
        self.openInSafari.enabled = true
    }
    
    // WebView がコンテンツの読み込みを完了した後に呼ばれる
    func webViewDidFinishLoad(webView: UIWebView) {
        // インジケータを非表示にする
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
        
        self.rewindButton.enabled = self.webView!.canGoBack
        self.fastForwardButton.enabled = self.webView!.canGoForward
    }
    
    // 戻るボタンの処理
    @IBAction func back(AnyObject) {
        self.webView?.goBack()
    }
    
    // 進むボタンの処理
    @IBAction func forward(AnyObject) {
        self.webView?.goForward()
    }
    
    // 再読み込みボタンの処理
    @IBAction func refresh(AnyObject) {
        self.webView?.reload()
    }
    
    // safari で開く
    @IBAction func safari(AnyObject) {
        let url = self.webView?.request?.URL
        UIApplication.sharedApplication().openURL(url!)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

ソースコード

kzy52/ios-swift-webview-example · GitHub

参考

http://iphone-tora.sakura.ne.jp/uikit_size.html

http://blog.impl.co.jp/post/60930914933/ios-autoresizingmask