Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

スワイプでページ切り替えを簡潔に実現する[Swift]

More than 5 years have passed since last update.

はじめに

チュートリアルでよくあるスワイプでページ切り替えについてです。
もともと、ページングのためにpageViewControllerが用意されていますが、使おうとすると、非常に煩雑なコードを多く書かなければなりません。(メインのコードが埋もれる)

この記事は、メインのコードを、簡潔化させるのに役立ちます。
実装的に魔法のような部分が多いですが(すみません)、メインのコードをかなり簡潔化できます!

環境

iOS
Swift 2.0 (Swift1.2のコードを、Xcode7.0で動作可能なように変更しただけです)
Xcode 7.0

サンプル

サンプル(ライブラリ?含む)はGitubで提供しています
ha1fha1f/PagingViewController

gitに慣れている方はお好きなところにclone、
慣れてない方はターミナルを起動して以下のコマンドを実行してください。DesktopにPagingViewControllerフォルダができるかと思います。

git
cd ~/Desktop
git clone https://github.com/ha1fha1f/PagingViewController.git

そのままサンプルのプロジェクトになっているので、実行可能です。
TutorialViewController_swift.png

PagingSample内がサンプル、PagingLibないがライブラリです。

このサンプルでは、チュートリアルのように5枚の写真が出てきて、最後のページだけStartボタンがある。そのStartを押すとViewControllerへと画面遷移する、という流れになっています。

最小限の利用方法

サンプルはほぼ最小構成になっています。

  1. PagingViewController/PagingViewControllerLibにある3つの.swiftファイルをプロジェクトに追加

    • PagingViewController.swift
    • PagingDataController.swift
    • PagingCellViewController.swift

  2. PagingCellViewControllerを継承したクラスをつくる
    これが各ページになります。
    最低限、

    override func setDataObject(dataObject: AnyObject?) {}
    

    をoverrideして、受け取ったdataObjectを適切にキャストしてフィールドとして保存してください
    その他は通常通りで、特殊な処理を書く必要はありません。

    サンプルを以下に示します。

    class ImagePageCellViewController: PagingCellViewController{
        var imagePath: String!
        override func setDataObject(dataObject: AnyObject?){
            if let tmpDataObject = dataObject {
                self.imagePath = tmpDataObject as! String
            }else{
                self.imagePath = "default.jpg"
            }
        }
    }
    


  3. PagingDataControllerを継承したクラスをつくる
    これが全てのページを管理するクラスのサポートをします。
    最低限、

    override func instanciateViewControllerAtIndex(index: Int) -> PagingCellViewController? {}
    

    をoverrideして、indexに対応した、PagingCellViewControllerを継承したクラスのインスタンスを生成してreturnしてください。
    indexは何ページ目かを示します(0〜)

    サンプルを以下に示します

    class TutorialPageDataController: PagingDataController {
        override func instanciateViewControllerAtIndex(index: Int) -> PagingCellViewController? {
            let dataViewController: PagingCellViewController
    
            // 最後のページだけ変えるサンプル
            if index == (self.pageData.count-1) {
                dataViewController = ImagePageCellLastViewController()
            }else{
                dataViewController = ImagePageCellViewController()
            }
    
            return dataViewController
        }
    }
    


  4. PagingViewControllerを継承したクラスをつくる
    これが全てのページをsubViewとしてもつViewControllerクラスになります。以下、親Controllerと呼びます。これもUIViewControllerを継承しています。
    最低限、PagingDataControllerを継承したクラスのインスタンスを作成して、setupWithDataControllerを実行する必要があります。

    サンプルを以下に示します

    class TutorialViewController: PagingViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let data = ["pic1.jpg", "pic2.jpg", "pic3.jpg", "pic4.jpg", "pic5.jpg"] 
            self.setupWithDataController(TutorialPageDataController(data: data) 
        }
    }
    


  5. これで最低限のページング処理を実現できます!

詳細

PagingCellViewController(を継承したクラス)

self.sendMessageToRoot("test")

管理しているViewControllerに対してメッセージ(String)を送れます(正確には、親ControllerクラスのreceiveMessage()メソッドを呼び出しています)
self.delegateに親Controllerを入れてあるので、これを使うこともできます
self.indexを見ると自分が何ページ目なのか取得できます

PagingDataControllerでindexに応じて異なるクラスを作成することができるので、継承したクラスはいくつあっても問題ありません。

PagingViewController(を継承したクラス)

これが親のcontrollerなので、この背景が、ページをスワイプしきったときの背景になります。逆に、どのページが表示されている時にも不動のもの(pageControlなど)を表示させることもできます。

ページングに関する設定項目は、

// ページの切り替わりのアニメーション
self.trasitionStyle = UIPageViewControllerTransitionStyle.Scroll

// ページ切り替わりの方向(縦横)
self.navigationOrientation = UIPageViewControllerNavigationOrientation.Horizontal

// PageControl(点が動く奴)が不要なときは
self.pageControl.removeFromSuperview()

self.pageControlの位置やサイズは、普通にいじれます

利用できるメソッドとしては、

// 特定のページヘジャンプする
moveTargetPage(index: Int)

// 今何ページ目を見ているか
getCurrentPageIndex() -> Int!

// 今見ているViewControllerのインスタンスを取得
getCurrentViewController() -> PagingCellViewController

オーバーライドできるメソッドとしては、

// 管理下のpageCellからのメッセージを受け取る
override func receiveMessage(message: String) {}

// ページ遷移後に呼ばれる。fromとtoはindexが渡される
override func afterPaging(from: Int, to: Int) {}

// ページ遷移前に呼ばれる。fromとtoはindexが渡される
override func beforePaging(from: Int, to: Int) {}

があります。

※いまPageControl用にafterPagingを用いているので、afterPagingのみsuper.afterPaging(from, to: to)を呼び出すようにお願いします

おわりに

PageControlをライブラリに組み込む必要はなかったかもしれません、笑
Cocoapodsでインストールできるようにする、みたいなことはできるんでしょうか

_ha1f
社会人4年目が終わろうとしているiOSエンジニア。 京都→博多→東京 書いてほしい内容リクエストあれば、Twitterなりでコメントいただければ書くかも。かも。
https://ha1f.net
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away