既存アプリを2ペイン化する using UISplitViewController


既存のテーブルビュー+詳細表示という構成のアプリをiPadおよびiPhone6s plus等の横幅が十分にある端末での使いやすさ向上のため、UISplitViewControllerを用いて2ペイン化してみました。手順は以下の通り

# UISplitViewControllerを用意する

```swift:M3CSplitViewControllerBase.swift
import UIKit

class M3CSplitViewControllerBase: UISplitViewController {
    var master: UIViewController?
    var detail: UIViewController?
    var childNavigationController: UINavigationController?
    var splitViewDelegate: UISplitViewControllerDelegate? { didSet { delegate = splitViewDelegate } }
    
    func createMaster() -> UIViewController? { return nil }
    func createDetail() -> UIViewController? { return nil }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        master = createMaster()
        childNavigationController = master.map(UINavigationController.init)
        splitViewDelegate = M3CSplitViewControllerDelegate()
        detail = createDetail()
        
        // iPadの場合は常に2ペイン表示にする
        // iPhoneの場合はiPhone 6 plus, 6s plusの横表示の場合だけ2ペインになる
        if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
            preferredDisplayMode = .AllVisible
        }
        
        self.viewControllers = [childNavigationController, detail].flatMap { $0 }
    }
}
```

```swift:M3CHogeSplitViewController.swift
import UIKit

class M3CHogeSplitViewController: M3CSplitViewControllerBase {
    
    override func createMaster() -> UIViewController? {
        let storyboard = UIStoryboard(name: M3CHogePageStoryboardString, bundle: nil)
        return storyboard.instantiateInitialViewController()
    }
    
    override func createDetail() -> UIViewController? {
        return M3CSplitWebViewController(url: nil)
    }
}
```

```swift:M3CSplitViewControllerDelegate.swift
import UIKit

protocol SplitDetailViewController {
    var isShowing: Bool { get }
}

protocol SplitMasterViewController {
    
}

class M3CSplitViewControllerDelegate: UISplitViewControllerDelegate {
    // Collapse the secondary view controller onto the primary view controller.
    @objc func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController, ontoPrimaryViewController primaryViewController: UIViewController) -> Bool {
        /*
        The secondary view is not showing. Return true to tell the
        splitViewController to use its default behavior: just hide the
        secondaryViewController and show the primaryViewController.
        */
        if (secondaryViewController as? SplitDetailViewController)?.isShowing != true {
            return true
        }
        return false
    }
    
    // Separate the secondary view controller from the primary view controller.
    @objc func splitViewController(splitViewController: UISplitViewController, separateSecondaryViewControllerFromPrimaryViewController primaryViewController: UIViewController) -> UIViewController? {
        if let nav = primaryViewController as? UINavigationController {
            nav.setNavigationBarHidden(true, animated: false)
        }
        return nil
    }
    
    @objc func splitViewController(splitViewController: UISplitViewController, showViewController vc: UIViewController, sender: AnyObject?) -> Bool {
        if let nav = vc as? UINavigationController {
            nav.setNavigationBarHidden(true, animated: false)
        }
        return false
    }
    
    @objc func splitViewController(splitViewController: UISplitViewController, showDetailViewController vc: UIViewController, sender: AnyObject?) -> Bool {
        if let nav = splitViewController.viewControllers.first as? UINavigationController {
            nav.setNavigationBarHidden(!splitViewController.collapsed, animated: false)
        }
        return false
    }
}
```

# 既存のTableViewControllerの詳細表示方法を修正する

```swift:M3CHogeTableViewController.swift
- navigationController?.pushViewController(vc, animated: true)
+ showDetailViewController(vc, sender: self) 
```

ついでに、navigationBarが出っぱなしだとかっこ悪いのでviewWillAppearに隠す処理を追加

```swift:M3CHogeTableViewController.swift viewWillAppear
navigationController?.setNavigationBarHidden(true, animated: false)
```

# 既存の詳細表示用ViewControllerに表示中かどうかの判定ロジック実装

```swift:M3CSplitWebViewController+SplitDetail.swift
extension M3CSplitWebViewController: SplitDetailViewController {
    // 表示中かどうかを返すメソッド, この例の場合はWebViewなので表示中かどうかをurlが空かどうかで判定
    var isShowing: Bool { get { return url != nil } }
}
```

って感じでどうかしら？