iOSアプリのviewの一部分に、ページコントロールで切り替えられるviewを埋め込む実装をしたので、メモ。
完成のイメージはこんな感じ。
動作環境
- 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アプリで、こんな感じの実装をしました。