PageMenuを使ってキュレーションアプリを作る（その2）【Swift3.0】

# キュレーションアプリを作る（その2）
横スライドのタブメニューライブラリPageMenuを使ったキュレーションアプリの実装手順を２回に分けて紹介します。本記事はその２回めです。今回はブラウズ機能を追加します。

[その１はこちら](http://qiita.com/fromage-blanc/items/4c358e1e57e298baad18)

## 1.ビューコントローラーとセグエの追加
起動時の各Webビュー内でブラウズできるようにしてもよいのですが、今回はブラウズ用のビューコントローラーを追加することにしましたので、そこに遷移するためのセグエも追加します。
<img width="1674" alt="2-1.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/1d4a3646-ec77-ed93-d1ac-0926afaf0c94.png">

## 2.セグエの設定
セグエのidentifierを設定します。今回は***browse***にしました。
<img width="1676" alt="2-2.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/f985f70e-05c4-91d2-67ba-8b37ac1377e3.png">

## 3.ブラウズ用クラスの追加
ブラウズ用のクラスを作成します。メニュー：File → New → Fileと辿り、「Cocoa Touch Class」をダブルクリック、`UIViewController`をサブクラスに選択します。今回は***BrowseViewController***という名前にしました。
<img width="724" alt="2-3.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/f0b0173f-6133-beaf-cd51-d6a311e95ed3.png">

## 4.ストーリーボード上のViewControllerと紐付け
ストーリーボード上のブラウズ用のビューコントローラーに`BrowseViewController`を割り当てます。
<img width="1674" alt="2-4.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/42b0be5c-02e3-eabd-584b-49d0ef95fdc6.png">

## 5.ブラウズクラスの実装
実装に伴い、***BackArrow***という戻るボタンのイメージセットを用意します。
イメージセットの作成には[ココ](http://qiita.com/fromage-blanc/items/552e47353ae21259e046#%EF%BC%94%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%82%BF%E3%83%96%E3%82%A2%E3%82%A4%E3%82%B3%E3%83%B3%E3%81%AE%E6%8C%87%E5%AE%9A)を参考にしてください。

**BrowseViewController.swif**

```swift
// Swift3.0

import UIKit

class BrowseViewController: UIViewController,UIWebViewDelegate {

    // 別ビューから受け取るリクエスト
    var request:URLRequest!
    
    // ウェブビュー
    var webView:UIWebView!
    
    // 「戻る」ボタンを配置するツールバー
    var toolbar:UIToolbar?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // ウェブビューを画面いっぱいに作成
        webView = UIWebView(frame: self.view.bounds)
        webView.delegate = self
    
        // URLリクエスト
        let req = URLRequest(url:request.url!)
        // ウェブビューにロード
        webView.loadRequest(req)
        // ウェブビューを表示
        self.view.addSubview(webView)
        
        // 「ホームへ戻る」ボタンの作成
        let button = UIButton(frame: CGRect(x:0,y:(self.view.bounds.height-40),width:self.view.bounds.width,height:40))
        button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
        button.setTitle("ホームへ戻る", for: .normal)
        button.setTitleColor(UIColor.white, for: .normal)
        button.backgroundColor = UIColor.lightGray
        button.addTarget(self, action: #selector(homeBack), for: .touchUpInside)
        self.view.addSubview(button)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {

        if navigationType == UIWebViewNavigationType.linkClicked {
            
            // ToolBarの制御
            if toolbar == nil {
                toolbar = UIToolbar(frame: CGRect(x:0,y:0,width:self.view.bounds.width,height:70))
                
                // Bar Button Item
                let img = UIImage(named:"BackArrow")!.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
                let item = UIBarButtonItem(image: img,
                                           style: UIBarButtonItemStyle.plain,
                                           target: nil,
                                           action: #selector(BrowseViewController.backBrowse))
                let items = [item]
                toolbar?.items = items
                
                self.view.addSubview(toolbar!)
            }
        }
        
        return true
    }
    
    func webViewDidFinishLoad(_ webView: UIWebView) {
        
        if webView.isLoading {
            return
        }
        
        toolbar?.isHidden = true
        
        // 戻るボタン
        if self.webView.canGoBack {
            toolbar?.isHidden = false
        } else {
            toolbar?.isHidden = true
        }
    }
    
    func backBrowse(){
        self.webView.goBack()
    }
    
    func homeBack() {
        self.dismiss(animated: true, completion: nil)
    }

}
```

## 6.ContentsViewControllerとViewControllerの改修
その１で作成済みのContentsViewController.swiftに以下を追加します。*masterViewPointer*はContentsViewControllerからViewController上のセグエを実行可能にするためのものです（protocol不要のデリゲートパタンのようなもの）。`func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool`はリクエストをフックするUIWebviewのデリゲートメソッドです。リクエストをフックしてリンククリックであれば、ViewContoroller上のbrowseメソッドを実行し、そのリクエストをキャンセル(return false)します。

```swift
  var masterViewPointer:ViewController?
```

```swift
  func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        if navigationType == UIWebViewNavigationType.linkClicked {
            masterViewPointer?.browse(request)
           return false
  }
```

追加後の**ContentsViewController.swift**

```swift
import UIKit

class ContentsViewController: UIViewController ,UIWebViewDelegate{

    var webView:UIWebView!
    var siteUrl:String!
    
    var masterViewPointer:ViewController?
    
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    

    // MARK: Webview delegate
    func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        
        if navigationType == UIWebViewNavigationType.linkClicked {
            masterViewPointer?.browse(request)
            return false
        }
        
        return true
    }
}
```

さらに、その１で作成したViewController.swiftに以下を追加します。ContentsViewControllerで発生したリクエストをViewControllerで受け取るための追加です。

```swift
var request:URLRequest?
```
```swift
controller.masterViewPointer = self
```

```swift
    func browse(_ request:URLRequest) {
        self.request = request
        performSegue(withIdentifier: "browse", sender: nil)
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "browse" {
            let vc = segue.destination as! BrowseViewController
            vc.request = self.request
        }
    }
```
追加後の**ViewController.swift**

```swift
import UIKit

class ViewController: UIViewController {

    // インスタンス配列
    var controllerArray : [UIViewController] = []
    var pageMenu : CAPSPageMenu?

    var request:URLRequest?

    // サイト情報
    let siteInfo:[Dictionary<String,String>] = [
        ["title":"ヤフー！知恵袋","url":"http://chiebukuro.yahoo.co.jp/"],
        ["title":"教えて！goo","url":"http://oshiete.goo.ne.jp/"],
        ["title":"OKWAVE","url":"http://okwave.jp/"],
        ["title":"発言小町","url":"http://komachi.yomiuri.co.jp/"],
        ["title":"BIGLOBEなんでも相談室","url":"http://soudan.biglobe.ne.jp/sp/"]
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        for site in siteInfo {
            
            let controller:ContentsViewController = ContentsViewController(nibName: "ContentsViewController", bundle: nil)
            
            controller.masterViewPointer = self
            
            controller.title = site["title"]!
            controller.siteUrl = site["url"]!

            controller.webView = UIWebView(frame : self.view.bounds)
            controller.webView.delegate = controller
            controller.view.addSubview(controller.webView)
            let req = URLRequest(url: URL(string:controller.siteUrl!)!)
            controller.webView.loadRequest(req)
            controllerArray.append(controller)
            
        }

        // Customize menu (Optional)
        let parameters: [CAPSPageMenuOption] = [
            .scrollMenuBackgroundColor(UIColor.white),
            .viewBackgroundColor(UIColor.white),
            .bottomMenuHairlineColor(UIColor.blue),
            .selectionIndicatorColor(UIColor.red),
            .menuItemFont(UIFont(name: "HelveticaNeue", size: 14.0)!),
            .centerMenuItems(true),
            .menuItemWidthBasedOnTitleTextWidth(true),
            .menuMargin(16),
            .selectedMenuItemLabelColor(UIColor.black),
            .unselectedMenuItemLabelColor(UIColor.gray)
            
        ]
        
        // Initialize scroll menu
        
        let rect = CGRect(origin: CGPoint(x: 0,y :20), size: CGSize(width: self.view.frame.width, height: self.view.frame.height))
        pageMenu = CAPSPageMenu(viewControllers: controllerArray, frame: rect, pageMenuOptions: parameters)
        
        self.addChildViewController(pageMenu!)
        self.view.addSubview(pageMenu!.view)
        
        pageMenu!.didMove(toParentViewController: self)
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func browse(_ request:URLRequest) {
        self.request = request
        performSegue(withIdentifier: "browse", sender: nil)
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "browse" {
            let vc = segue.destination as! BrowseViewController
            vc.request = self.request
        }
    }
}
```

## 7.ビルドしてみる
起動後にロードされるウェブビュー内のリンクをクリックするとBrowseViewへ遷移し、そのBrowseViewが持つwebView内でリクエストが展開されます。
<img width="643" alt="3-2.png" src="https://qiita-image-store.s3.amazonaws.com/0/151314/f050c860-6b2a-7ee9-b55b-e95fc596b8ff.png">

## 8.補足
今回、戻るボタン付きツールバーの表示・非表示は、`UIWebViewNavigationType.linkClicked`のみをフックして判定をおこなっています。フォームのポスト時などには対応していません。

#### 参考:UIWebViewNavigationType

```swift
public enum UIWebViewNavigationType : Int {

    case linkClicked

    case formSubmitted

    case backForward

    case reload

    case formResubmitted

    case other
}
```

プロジェクト一式を[GitHub](https://github.com/fromageblanc/CurationAppSample)に置いときます