iOS
Swift

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

More than 3 years have passed since last update.

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


UISplitViewControllerを用意する


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 }
}
}



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)
}
}



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の詳細表示方法を修正する


M3CHogeTableViewController.swift

- navigationController?.pushViewController(vc, animated: true)

+ showDetailViewController(vc, sender: self)

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

swift:M3CHogeTableViewController.swift viewWillAppear

navigationController?.setNavigationBarHidden(true, animated: false)


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


M3CSplitWebViewController+SplitDetail.swift

extension M3CSplitWebViewController: SplitDetailViewController {

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

って感じでどうかしら?