【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」になります。
下記コードが、動くモノになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | // 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 } } } |
コード①
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上に配置しないのであれば、こちらも作成していく必要がありますが。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | // 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 } } } |
コード①
最初に、[ UIPageViewContorller ]のクラスを作成します。
コード②
次に、表示するViewControllerを作成します。使い捨てになるので、インスタンス生成&破棄を容易に行えるfor文中に記載した方が良いでしょう。
電子書籍リーダとかは、ここにUIImageを配置して、画像を読み込ませる処理をするとかですね。
コードでラベル配置したり、ボタン配置したりと色々するのであれば、StoryBoardからやった方が早いと思います。
コード③
次は、UIPageViewControllerのインスタンス生成です。最初に定義してあげただけなので、生成します。
ここで、遷移アニメーションやナビゲーション方向を決めます。
コード④
次に、既存のViewControllerにPageViewControllerを追加すればOKです。
最後にPageViewDataSourceを定義してあげればOKです。これはStoryBoardで定義したコードと同一になります。
PageControllerの使用方法
PageController
上部画像の「この部分」と吹き出しにした枠の箇所がPageControllerになります。
現在ページの場所と、全ページ数が視覚的にわかるコントローラです。
また、PageControllerですが、StoryBoardで配置するよりコードから作成した方が簡単なので、紹介はコードからのみにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | // 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)! } } |
コード①
まずは、PageControllerのカレントページを切り替えるために、PageViewControllerのDelegeteを追加します。
Delegateを追加しなくても管理はできますが、左右スワイプのイベントでも管理できますが、似た処理を別々のメソッドに記載するので、管理が煩雑になるのを防ぐため、Delegateで管理します。
コード②
次に、PageControllerの初期化を行います。最低限の配置場所、全ページ数、(初期)表示ページ、インジケータ色と現在ページのインジケータ色を設定すれば問題ないと思います。
コード③
次に、カレントページ切り替えのために、UIPageViewControllerのDelegateを追加します。
コード④
最後に、切り替えをするためのイベントを追加します。UIPageViewControllerのアニメーション終了後が妥当かと思いますので、[ pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) ]を追加します。
これで、ページを遷移した際にインジケータの色が変更されます。
UIPageViewController及びUIPageControlの使い方は以上になります。
それではまたの機会に!
↓↓↓ぜひチェックしてください
~提供中のヒューマンセンシング技術~
◆人物検出技術
歩行者・来店者数計測やロボット搭載も
https://humandetect.pas-ta.io
◆視線検出技術
アイトラッキングや次世代UIに
https://eyetrack.pas-ta.io
◆生体判定技術
eKYC・顔認証のなりすまし対策を!
https://bio-check.pas-ta.io
◆目検出技術
あらゆる目周りデータを高精度に取得
https://pupil.pas-ta.io
◆音声感情認識技術
会話から怒りや喜びの感情を判定
https://feeling.pas-ta.io
◆虹彩認証技術
目の虹彩を利用した生体認証技術
https://iris.pas-ta.io