LoginSignup
3

More than 3 years have passed since last update.

【swift】pageViewControllerでページ遷移時に行いたい処理の記述場所は?

Last updated at Posted at 2020-01-12

前置き

スワイプによるページ遷移、ボタン押下によるページ遷移、までは実装できたものの、スワイプによるページ遷移時の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をつけている

スクリーンショット 2020-01-12 20.14.45.png

コード

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()

        // 最初は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: [])

    }
}
PageViewController.swift
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:)

実装

PageViewController.swift
    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に書き足し。

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で繋ぎ、スワイプでページ遷移

【決定版】UIPageViewControllerの使い方(Swift)

タブを作成して、タブ操作とページ遷移を紐付ける

UICollectionViewControllerとUIPageViewControllerでSmartNewsっぽいあのUIをお手軽に実現する

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3