【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から設定していく方法から紹介します。

  1. StoryBoard上に「PageViewController」を追加。
  2. PageViewControllerを選択し、「Is Initial ViewController」をチェック。
  3. 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のカレントページを切り替えるために、PageViewControllerDelegeteを追加します。
Delegateを追加しなくても管理はできますが、左右スワイプのイベントでも管理できますが、似た処理を別々のメソッドに記載するので、管理が煩雑になるのを防ぐため、Delegateで管理します。

 

コード②

次に、PageControllerの初期化を行います。最低限の配置場所全ページ数(初期)表示ページインジケータ色現在ページのインジケータ色を設定すれば問題ないと思います。

 

コード③

次に、カレントページ切り替えのために、UIPageViewControllerのDelegateを追加します。

 

コード④

最後に、切り替えをするためのイベントを追加します。UIPageViewControllerのアニメーション終了後が妥当かと思いますので、[ pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) ]を追加します。

 

これで、ページを遷移した際にインジケータの色が変更されます。

 


 

UIPageViewController及びUIPageControlの使い方は以上になります。
それではまたの機会に!


Top