前置き
スワイプによるページ遷移、ボタン押下によるページ遷移、までは実装できたものの、スワイプによるページ遷移時のtopviewのデザインの変更ができないとつまずいた人間の、備忘録。
環境
xcode 11.3
swift 5.1.3
実現したい機能
- スワイプでページ遷移 : 完了
- ボタン押下でページ遷移 : 完了
- ページ遷移に従い、ページ遷移しない部分のデザインの変更 : これ!!!
現状
ファイル構成
- TopViewController.swift <- 元となるViewController
- PageViewController.swift <- page遷移機能をまとめたViewController
- View1Controller.swift
- View2Controller.swift
- View3Controller.swift
storybord
- 元となるViewControllerが存在
- その中に、3つのボタンと、ページのタイトルラベル、そしてcontainerViewが存在
- containerViewの先はpageViewControllerとなっている。
- 遷移先のページとして、viewControllrが3つ(view1,view2,view3)存在している。
- 各viewには、それぞれtopView,view1,view2,view3のstorybordIDをつけている
コード
import UIKit
class TopViewController: UIViewController {
// pageのタイトル
@IBOutlet weak var pageTitle: UILabel!
// buttons
@IBOutlet weak var toView1Btn: boundButton!
@IBOutlet weak var toView2Btn: boundButton!
@IBOutlet weak var toView3Btn: boundButton!
// toView1Btn押下時
@IBAction func toView1Action(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "view1") as! View1Controller
pageViewController!.setViewControllers([vc], direction: .forward, animated: false, completion: nil)
toView1Design()
}
// toView2Btn押下時
@IBAction func toView2Action(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "view2") as! View2Controller
pageViewController!.setViewControllers([vc], direction: .reverse, animated: false, completion: nil)
toView2Design()
}
// toView3Btn押下時
@IBAction func toView3Action(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "view3") as! View3Controller
pageViewController!.setViewControllers([vc], direction: .reverse, animated: false, completion: nil)
toView3Design()
}
// 画面遷移用
var pageViewController: UIPageViewController?
override func viewDidLoad() {
super.viewDidLoad()
// 最初はview1を出しとく
toView1Design()
}
}
// デザイン変更用関数のまとめ
extension TopViewController {
// view1に遷移するとき呼ばれる
func toView1Design() {
// pageTitle変更
pageTitle.text = "view1"
// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)
toView1Btn.setTitleColor(UIColor.red, for: [])
toView2Btn.setTitleColor(UIColor.lightGray, for: [])
toView3Btn.setTitleColor(UIColor.lightGray, for: [])
}
// view2に遷移するとき呼ばれる
func toView2Design() {
// pageTitle変更
pageTitle.text = "view2"
// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)
toView1Btn.setTitleColor(UIColor.lightGray, for: [])
toView2Btn.setTitleColor(UIColor.red, for: [])
toView3Btn.setTitleColor(UIColor.lightGray, for: [])
}
// view3に遷移するとき呼ばれる
func toView3Design() {
// pageTitle変更
pageTitle.text = "view3"
// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)
toView1Btn.setTitleColor(UIColor.lightGray, for: [])
toView2Btn.setTitleColor(UIColor.lightGray, for: [])
toView3Btn.setTitleColor(UIColor.red, for: [])
}
}
import UIKit
class PageViewController: UIPageViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.setViewControllers([getView1()], direction: .forward, animated: true, completion: nil)
self.dataSource = self
}
// それぞれ、viewControllerを返す関数
func getView1() -> View1Controller {
return storyboard!.instantiateViewController(withIdentifier: "view1") as! View1Controller
}
func getView2() -> View2Controller {
return storyboard!.instantiateViewController(withIdentifier: "view2") as! View2Controller
}
func getView3() -> View3Controller {
return storyboard!.instantiateViewController(withIdentifier: "view3") as! View3Controller
}
func getTop() -> TopViewController {
return storyboard!.instantiateViewController(withIdentifier: "topView") as! TopViewController
}
}
//datasource用の関数まとめ
extension PageViewController : UIPageViewControllerDataSource {
// 現在表示されているページの、Beforeに位置する、つまり左側に位置するviewを呼び出す関数
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
print(viewController)
if viewController.isKind(of : View3Controller.self) {
// 3の時 -> 2
return getView2()
}
if viewController.isKind(of : RepoViewController.self) {
// 2の時 -> 1
return getView1()
}
if viewController.isKind(of : CalendarViewController.self) {
// 1の時 -> 3
return getView3()
}
return nil
}
// 現在表示されているページの、Afterに位置する、つまり右側に位置するviewを呼び出す関数
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
print(viewController)
if viewController.isKind(of : CalendarViewController.self) {
// 1の時 -> 2
return getView2()
}
if viewController.isKind(of : RepoViewController.self) {
// 2の時 -> 3
return getView3()
}
if viewController.isKind(of : UserViewController.self) {
// 3の時 -> 1
return getView1()
}
return nil
}
}
課題
スワイプによるページ遷移に伴うTopViewController上のデザインの変更メソッドを呼ぶ場所がわからない。
datasource用の関数が呼ばれるタイミングは、現在表示されているviewcontrollerとずれている。
解決策
検索結果
調べた結果、delegateメソッドでスワイプしたタイミングを検知してくれるメソッドがあるらしい。これを使う。
UIPageViewControllerでページ遷移時に処理したいことを書くときのTip
Apple Developer : pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:)
実装
override func viewDidLoad() {
super.viewDidLoad()
self.setViewControllers([getView1()], direction: .forward, animated: true, completion: nil)
self.dataSource = self
// ここから追記
self.delegate = self
// ここまで追記
}
// 以下を追記
extension PageViewController : UIPageViewControllerDelegate{
// スワイプによるページ遷移が行われたときに呼ばれるメソッド
func pageViewController(_ pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool){
// 現在のviewcontrollerを取得
let currentVC = pageViewController.viewControllers![0]
// topViewControllerを取得
let topView = getTop()
// デザインを変更処理
if currentVC!.isKind(of : View1Controller.self) {
topView.toView1Design()
}
if currentVC!.isKind(of : View2Controller.self) {
topView.toView2Design()
}
if currentVC!.isKind(of : View3Controller.self) {
topView.toView3Design()
}
}
}
課題2
しかしながらこのコードでは、デザイン変更関数が呼ばれた時に、toView1Btnがない、と言ったエラーが発生する。
pageViewController上でデザイン変更関数のみを呼び出しており、TopViewController上で行われている変数設定等を行っていないからだ。
解決策2
検索結果2
どうやら、pageVioewControllerをTopViewController内部で設定する方法もあるらしい。
この方法を用いれば、上記のエラーも発生しなくなるはず。
qiita : 【Swift】UIPageViewControllerとContainerViewを利用してタップで画面遷移
実装2
PageViewController.swiftを削除し、TopViewController.swiftに書き足し。
import UIKit
class TopViewController: UIViewController {
// pageのタイトル
@IBOutlet weak var pageTitle: UILabel!
// buttons
@IBOutlet weak var toView1Btn: boundButton!
@IBOutlet weak var toView2Btn: boundButton!
@IBOutlet weak var toView3Btn: boundButton!
// toView1Btn押下時
@IBAction func toView1Action(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "view1") as! View1Controller
pageViewController!.setViewControllers([vc], direction: .forward, animated: false, completion: nil)
toView1Design()
}
// toView2Btn押下時
@IBAction func toView2Action(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "view2") as! View2Controller
pageViewController!.setViewControllers([vc], direction: .reverse, animated: false, completion: nil)
toView2Design()
}
// toView3Btn押下時
@IBAction func toView3Action(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "view3") as! View3Controller
pageViewController!.setViewControllers([vc], direction: .reverse, animated: false, completion: nil)
toView3Design()
}
// 画面遷移用
var pageViewController: UIPageViewController?
override func viewDidLoad() {
super.viewDidLoad()
// ここから追加
// PageViewControllerの設定
pageViewController = children.first! as? UIPageViewController
pageViewController!.setViewControllers([getView1()], direction: .forward, animated: true, completion: nil)
pageViewController!.dataSource = self
pageViewController!.delegate = self
// ここまで追加
// 最初はview1を出しとく
toView1Design()
}
}
// デザイン変更用関数のまとめ
extension TopViewController {
// view1に遷移するとき呼ばれる
func toView1Design() {
// pageTitle変更
pageTitle.text = "view1"
// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)
toView1Btn.setTitleColor(UIColor.red, for: [])
toView2Btn.setTitleColor(UIColor.lightGray, for: [])
toView3Btn.setTitleColor(UIColor.lightGray, for: [])
}
// view2に遷移するとき呼ばれる
func toView2Design() {
// pageTitle変更
pageTitle.text = "view2"
// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)
toView1Btn.setTitleColor(UIColor.lightGray, for: [])
toView2Btn.setTitleColor(UIColor.red, for: [])
toView3Btn.setTitleColor(UIColor.lightGray, for: [])
}
// view3に遷移するとき呼ばれる
func toView3Design() {
// pageTitle変更
pageTitle.text = "view3"
// ボタンの色変更(選ばれているボタンを赤色に、それ以外は灰色に)
toView1Btn.setTitleColor(UIColor.lightGray, for: [])
toView2Btn.setTitleColor(UIColor.lightGray, for: [])
toView3Btn.setTitleColor(UIColor.red, for: [])
}
}
// ここから下全部追記
// view取得系の関数まとめ
extension TopViewController {
// それぞれ、viewControllerを返す関数
func getView1() -> View1Controller {
return storyboard!.instantiateViewController(withIdentifier: "view1") as! View1Controller
}
func getView2() -> View2Controller {
return storyboard!.instantiateViewController(withIdentifier: "view2") as! View2Controller
}
func getView3() -> View3Controller {
return storyboard!.instantiateViewController(withIdentifier: "view3") as! View3Controller
}
func getTop() -> TopViewController {
return storyboard!.instantiateViewController(withIdentifier: "topView") as! TopViewController
}
}
// datasource用の関数まとめ
extension TopViewController : UIPageViewControllerDataSource {
// 現在表示されているページの、Beforeに位置する、つまり左側に位置するviewを呼び出す関数
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
print(viewController)
if viewController.isKind(of : View3Controller.self) {
// 3の時 -> 2
return getView2()
}
if viewController.isKind(of : RepoViewController.self) {
// 2の時 -> 1
return getView1()
}
if viewController.isKind(of : CalendarViewController.self) {
// 1の時 -> 3
return getView3()
}
return nil
}
// 現在表示されているページの、Afterに位置する、つまり右側に位置するviewを呼び出す関数
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
print(viewController)
if viewController.isKind(of : CalendarViewController.self) {
// 1の時 -> 2
return getView2()
}
if viewController.isKind(of : RepoViewController.self) {
// 2の時 -> 3
return getView3()
}
if viewController.isKind(of : UserViewController.self) {
// 3の時 -> 1
return getView1()
}
return nil
}
}
// delegate用の関数まとめ
extension TopViewController : UIPageViewControllerDelegate{
// スワイプによるページ遷移が行われたときに呼ばれるメソッド
func pageViewController(_ pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool){
// 現在のviewcontrollerを取得
let currentVC = pageViewController.viewControllers?.first!
if currentVC!.isKind(of : View1Controller.self) {
toView1Design()
}
if currentVC!.isKind(of : View2Controller.self) {
toView2Design()
}
if currentVC!.isKind(of : View3Controller.self) {
toView3Design()
}
}
}
参考サイト
二つの画面をpageViewControllerで繋ぎ、スワイプでページ遷移
タブを作成して、タブ操作とページ遷移を紐付ける
UICollectionViewControllerとUIPageViewControllerでSmartNewsっぽいあのUIをお手軽に実現する