Posted at

コンテナview controllerを利用して、viewの一部にページコントロールを埋め込む

More than 3 years have passed since last update.

iOSアプリのviewの一部分に、ページコントロールで切り替えられるviewを埋め込む実装をしたので、メモ。

完成のイメージはこんな感じ。

page_control_container.gif


動作環境


  • Xcode 7.3

  • iOS 9.3


実装


親view

まず親viewから。


ViewController.swift

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let label = UILabel()
label.frame.size = CGSize(width: 200, height: 100)
label.center = view.center
label.text = "ベースのview"
label.textAlignment = .Center
view.addSubview(label)

// コンテナに追加
let pageVC = PageViewController(transitionStyle: .Scroll,
navigationOrientation: .Horizontal,
options: nil)
let pageViewHeight: CGFloat = 100
pageVC.view.frame = CGRect(x: 0, y: view.bounds.size.height - pageViewHeight, width: view.bounds.size.width, height: pageViewHeight)

addChildViewController(pageVC)
view.addSubview(pageVC.view)
pageVC.didMoveToParentViewController(self)
}

}


PageViewController(後述) のインスタンスを、子viewのview controllerとして利用。



  • addChildViewController(_:)で、子のview controllerとして上記のPageViewControllerを追加

  • 子のviewを自身のviewに追加


  • didMoveToParentViewController(_:)を明示的に呼び出してview controllerの追加を終了する


子view

親viewの中に埋め込むview controllerを作成。

複数のviewを含むページコントロールとして実装するので、UIPageViewControllerを継承させる。同時にUIPageViewControllerDataSource, UIPageViewControllerDelegateを適用。


PageViewController.swift

import UIKit

class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

var contentVCs = [UIViewController]()
var currentIndex: Int = 0

// MARK: - View lifecycle

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = UIColor.blackColor()
dataSource = self
delegate = self

for i in 0..<3 {
let contentVC = PageContentViewController()
contentVC.contentNumber = i
contentVCs.append(contentVC)
}
setViewControllers([contentVCs[0]], direction: .Forward, animated: true, completion: nil)
}

// MARK: - UIPageViewControllerDataSource

func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
guard let index = contentVCs.indexOf(viewController) where index != NSNotFound else { return nil }
if index < contentVCs.count - 1 {
return contentVCs[index + 1]
} else {
return contentVCs.first
}
}

func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
guard let index = contentVCs.indexOf(viewController) where index != NSNotFound else { return nil }
if index > 0 {
return contentVCs[index - 1]
} else {
return contentVCs.last
}
}

func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return contentVCs.count
}

func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return currentIndex
}

// MARK: - UIPageViewControllerDelegate

func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard finished else { return }
guard let contentVC = pageViewController.viewControllers?.first else { return }
guard let index = contentVCs.indexOf(contentVC) where index != NSNotFound else { return }

currentIndex = index
}

}




  • viewDidLoad()で、ページコントロールの中身となるview controllerを3つセット。



    • setViewControllers(_:direction:animated:completion:)を利用




  • pageViewController(_:viewControllerBeforeViewController:), pageViewController(_:viewControllerAfterViewController:)で、表示中の前後のview controllerを指定。


    • 今回は行き止まりを作らず、無限にスクロールさせるようになっている




  • presentationCountForPageViewController(_:)で、ページインジケータの数をセット


    • 3個




  • presentationIndexForPageViewController(_:)でページインジケータのインデックス(位置)をセット


    • ページインジケータの現在のインデックス (currentIndex) を返す




  • pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:)で、ページのtransitionが終了した時の処理を記述



    • currentIndexを更新




ページコントロールのコンテンツ部分のview controllerは以下のように実装。

contentNumberという変数を生成時に受け取り、それをページ番号として表示している。


PageContentViewController.swift

class PageContentViewController: UIViewController {

var contentNumber: Int?

override func viewDidLoad() {
super.viewDidLoad()

guard let contentNumber = contentNumber else { return }
let label = UILabel(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 100))
label.text = "\(contentNumber + 1)枚目"
label.textColor = UIColor.whiteColor()
label.textAlignment = .Center
view.addSubview(label)
}
}



おわり

以上で実装完了。

もう少し複雑ですが、yentaというiOSアプリで、こんな感じの実装をしました。


参考