【Swift】Swift入門 ~ UIPageViewControllerを使ってみる ~
2020.03.13

どうも、むつたくです。
今回は「UIPageViewController」について紹介していきます。
また画面遷移系ですが、アプリ初回起動時とかにアプリ説明(チュートリアル)をこのUIPageViewControllerを使って表示させたり、
読書アプリなんかの電子書籍リーダにも使用でき、用途は様々です。
前回までのSwift記事
【Swift】Swift入門 ~ UITableViewを使ってみる ~
【Swift】Swift入門 ~ NavigationControllerを使ってみる ~
【Swift】Swift入門 ~ presentの画面遷移 ~
今回やること
公式ドキュメントは以下です。
UIPageViewController
UIPageControl
開発環境
- macOS Catalina(10.15.3)
- Xcode 11.3
- Swift 5.1
- Simulater iPhone 11Pro
- iOS 13.2
今回も、XcodeのProjectで、SingleViewAppとして作成します。
基本的な使用方法
StoryBoardからの使用方法 – コントローラの配置
まずは、StoryBoardから設定していく方法から紹介します。
- StoryBoard上に「PageViewController」を追加。
- PageViewControllerを選択し、「Is Initial ViewController」をチェック。
- PageViewController.swiftファイルを追加。


次に遷移先のViewControllerを2枚ほど配置していきます。
2枚目のStoryBoradIDを[ SecondView ]に、3枚目を[ ThirdView ]にします。遷移の見分けも可能な様に背景色を設定します。

ついでに追加したViewControllerのClassファイルを作成しましょう。
[ Cocoa Touch Class > Class:(SecondViewController) > SubclassOf:(UIViewController) ]
3枚目は[ Class:(ThirdViewController) ]にします。他は同じです。
そうしましたら、追加した2ファイルをSecondView及び、ThirdViewに関連付けします。
「Use StoryBoardID」のチェックも忘れずに。

これでStoryBoradはOKです。次にコードです。
StoryBoardからの使用方法 – コーディング
主に処理を記載するのは最初に追加した「PageViewController.swift」になります。
下記コードが、動くモノになります。
[cc_swift]
// PageViewController.swift
import UIKit
class PageViewController: UIPageViewController {
// ① PageViewで表示するViewControllerを格納する配列を定義
private var controllers: [UIViewController] = []
override func viewDidLoad() {
super.viewDidLoad()
self.initPageViewController()
}
/// PageViewControllerの初期化処理
private func initPageViewController() {
// ② PageViewControllerで表示するViewControllerをインスタンス化する
let firstVC = storyboard!.instantiateViewController(withIdentifier: “FirstView”) as! ViewController
let secondVC = storyboard!.instantiateViewController(withIdentifier: “SecondView”) as! SecondViewController
let ThirdVC = storyboard!.instantiateViewController(withIdentifier: “ThirdView”) as! ThirdViewController
// ③ インスタンス化したViewControllerを配列に保存する
self.controllers = [ firstVC, secondVC, ThirdVC ]
// ④ 最初に表示するViewControllerを指定する
setViewControllers([self.controllers[0]], direction: .forward, animated: true, completion: nil)
// ④ PageViewControllerのDataSourceを関連付ける
self.dataSource = self
}
}
// ⑤ PageViewControllerのDataSourceを定義
// MARK: – UIPageViewController DataSource
extension PageViewController: UIPageViewControllerDataSource {
/// ページ数
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return self.controllers.count
}
/// 左にスワイプ(進む)
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if let index = self.controllers.firstIndex(of: viewController),
index < self.controllers.count - 1 {
return self.controllers[index + 1]
} else {
return nil
}
}
/// 右にスワイプ (戻る)
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let index = self.controllers.firstIndex(of: viewController),
index > 0 {
return self.controllers[index – 1]
} else {
return nil
}
}
}
[/cc_swift]
コード①
PageViewControllerでは、表示するViewControllerを配列で管理するため、クラス内グローバル関数を定義します。
コード②
次は、表示対象のViewControllerをインスタンス化します。
コード③
次に、最初に定義した配列にインスタンス化したViewControllerを格納します。
この時、表示順に格納していくのがポイントです。インデックスのインクリメント、デクリメントで表示を切り替えるためです。
コード④
次は、最初に表示する画面を設定します。設定するプロパティは[ setViewControllers ]です。
- 第一引数: 最初に表示するViewController
- 第二引数: ナビゲーションの指定
- 第三引数: アニメーション有無
- 第四引数: コールバック関数
詳しくは公式ドキュメントを確認してください。
setViewControllers(_:direction:animated:completion:)
コード⑤
最後に、PageViewControllerのDataSourceを関連付けます。
DataSourceでやっていることは、ページ数の上限設定、左右スワイプの挙動になります。
ページ数の上限は[ presentationCount ]で行います。配列のインデックス数を指定してあげればOKです。
左スワイプの挙動は[ pageViewController(viewControllerAfter) ]で行います。引数内にAfterが付いている関数です。
右スワイプの挙動は[ pageViewController(viewControllerBefore) ]で行います。引数内にBeforeが付いている関数です。
やっていることは単純です。
左スワイプ検知時、最初に定義した配列での現在表示しているViewControllerのインデックスを取得し、上限値を超過していなければ、次ページのインデックスを渡しています。
右スワイプ検知時は、同じく表示中のViewControllerのインデックスを取得し、0未満(最初のページ)ではない時に、前ページのインデックスを渡してます。
以上がStoryBoardを使用した場合です。
コードからの実装
コードからの実装とStoryBoardからの実装の違いは、PageViewControllerのインスタンスをコード上に書くことぐらいでしょうか。
ViewControllerをStoryBoard上に配置しないのであれば、こちらも作成していく必要がありますが。
[cc_swift]
// ViewController.swift
import UIKit
class ViewController: UIViewController {
// ① PageViewControllerクラス、
// PageViewで表示するViewControllerを格納する配列をそれぞれ定義
private var pageViewController: UIPageViewController!
private var controllers: [ UIViewController ] = []
override func viewDidLoad() {
super.viewDidLoad()
self.initPageViewController()
}
private func initPageViewController() {
// 背景色定義
let backColor: [ UIColor ] = [ .systemIndigo, .systemOrange, .systemGreen ]
// ② 表示するViewController作成 & 表示配列に保存
for i in 0 … 2 {
let myViewController: UIViewController = UIViewController()
myViewController.view.backgroundColor = backColor[i]
myViewController.view.frame = self.view.frame
self.controllers.append(myViewController)
}
// ③ UIPageViewController設定
self.pageViewController = UIPageViewController(transitionStyle: .pageCurl, navigationOrientation: .horizontal, options: nil)
self.pageViewController.setViewControllers([self.controllers[0]], direction: .forward, animated: true, completion: nil)
self.pageViewController.dataSource = self
// ④既存ViewControllerに追加
self.addChild(self.pageViewController)
self.view.addSubview(self.pageViewController.view!)
}
}
// ⑤ PageViewControllerのDataSourceを定義
// MARK: – UIPageViewController DataSource
extension ViewController: UIPageViewControllerDataSource {
/// ページ数
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return self.controllers.count
}
/// 左にスワイプ(進む)
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if let index = self.controllers.firstIndex(of: viewController),
index < self.controllers.count - 1 {
return self.controllers[index + 1]
} else {
return nil
}
}
/// 右にスワイプ (戻る)
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if let index = self.controllers.firstIndex(of: viewController),
index > 0 {
return self.controllers[index – 1]
} else {
return nil
}
}
}
[/cc_swift]
コード①
最初に、[ UIPageViewContorller ]のクラスを作成します。
コード②
次に、表示するViewControllerを作成します。使い捨てになるので、インスタンス生成&破棄を容易に行えるfor文中に記載した方が良いでしょう。
電子書籍リーダとかは、ここにUIImageを配置して、画像を読み込ませる処理をするとかですね。
コードでラベル配置したり、ボタン配置したりと色々するのであれば、StoryBoardからやった方が早いと思います。
コード③
次は、UIPageViewControllerのインスタンス生成です。最初に定義してあげただけなので、生成します。
ここで、遷移アニメーションやナビゲーション方向を決めます。
コード④
次に、既存のViewControllerにPageViewControllerを追加すればOKです。
最後にPageViewDataSourceを定義してあげればOKです。これはStoryBoardで定義したコードと同一になります。
PageControllerの使用方法
PageController
上部画像の「この部分」と吹き出しにした枠の箇所がPageControllerになります。
現在ページの場所と、全ページ数が視覚的にわかるコントローラです。
また、PageControllerですが、StoryBoardで配置するよりコードから作成した方が簡単なので、紹介はコードからのみにします。
[cc_swift]
// ViewController.swift
class ViewController: UIViewController {
private var pageViewController: UIPageViewController!
private var controllers: [ UIViewController ] = []
// ① UIPageControlクラスを定義
private var pageControl: UIPageControl!
override func viewDidLoad() {
super.viewDidLoad()
self.initPageViewController()
// UIPageControlの初期化メソッドを作成
self.setPageControl()
}
private func initPageViewController() {
let backColor: [ UIColor ] = [ .white, .systemOrange, .systemGreen ]
for i in 0 … 2 {
let myViewController: UIViewController = UIViewController()
myViewController.view.backgroundColor = backColor[i]
myViewController.view.frame = self.view.frame
self.controllers.append(myViewController)
}
self.pageViewController = UIPageViewController(transitionStyle: .pageCurl, navigationOrientation: .horizontal, options: nil)
self.pageViewController.setViewControllers([self.controllers[0]], direction: .forward, animated: true, completion: nil)
self.pageViewController.dataSource = self
// ① delegate追加
self.pageViewController.delegate = self
self.addChild(self.pageViewController)
self.view.addSubview(self.pageViewController.view!)
}
// ② [ setPageControl ]メソッド追加
private func setPageControl() {
// PageControlの配置場所
self.pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY – 190, width: UIScreen.main.bounds.width,height: 50))
// 全ページ数
self.pageControl.numberOfPages = self.controllers.count
// 表示ページ
self.pageControl.currentPage = 0
// インジケータの色
self.pageControl.pageIndicatorTintColor = .gray
// 現在ページのインジケータの色
self.pageControl.currentPageIndicatorTintColor = .red
self.view.addSubview(self.pageControl)
}
}
// MARK: – UIPageViewController DataSource
extension ViewController: UIPageViewControllerDataSource {
// 省略
}
// ③ [ UIPageViewControllerDelegate ]追加
// MARK: – UIPageViewController Delegate
extension ViewController: UIPageViewControllerDelegate {
// ④ アニメーション終了後処理 追加
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let currentPage = pageViewController.viewControllers![0]
self.pageControl.currentPage = self.controllers.firstIndex(of: currentPage)!
}
}
[/cc_swift]
コード①
まずは、PageControllerのカレントページを切り替えるために、PageViewControllerのDelegeteを追加します。
Delegateを追加しなくても管理はできますが、左右スワイプのイベントでも管理できますが、似た処理を別々のメソッドに記載するので、管理が煩雑になるのを防ぐため、Delegateで管理します。
コード②
次に、PageControllerの初期化を行います。最低限の配置場所、全ページ数、(初期)表示ページ、インジケータ色と現在ページのインジケータ色を設定すれば問題ないと思います。
コード③
次に、カレントページ切り替えのために、UIPageViewControllerのDelegateを追加します。
コード④
最後に、切り替えをするためのイベントを追加します。UIPageViewControllerのアニメーション終了後が妥当かと思いますので、[ pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) ]を追加します。
これで、ページを遷移した際にインジケータの色が変更されます。
UIPageViewController及びUIPageControlの使い方は以上になります。
それではまたの機会に!
↓↓↓ぜひチェックしてください
~提供中のヒューマンセンシング技術~
◆なりすまし判定技術
eKYC・顔認証のなりすまし対策を!
https://bio-check.pas-ta.io
◆まばたき検出技術
PCやスマホアプリに組み込めて高精度にまばたきを検出
https://blink.pas-ta.io
◆視距離推定技術
単一RGBカメラで、目からカメラまでの距離を推定
https://vd.pas-ta.io
◆虹彩認証技術
目の虹彩を利用した生体認証技術
https://iris.pas-ta.io
◆視線検出技術
アイトラッキングや次世代UIに
https://eyetrack.pas-ta.io
◆目検出技術
あらゆる目周りデータを高精度に取得
https://pupil.pas-ta.io
