LoginSignup
41
42

More than 5 years have passed since last update.

[Swift] AndroidでいうViewPager的な左右にスワイプしてviewを切り替えるサンプルを書いてみた

Last updated at Posted at 2015-04-08

ここ1,2年でよく見るようになったUIパターンかなと思います。objcで書いてあったものを、練習がてらswiftで書きなおしてみました。

(追記: 2015/04/09)
なんと!UIPageViewControllerなるものがありました...
長所/短所あるかもしれませんが、こちらで実装するのが良いのかもしれません。
http://qiita.com/yd_niku/items/b9f10bf5071971a1d8da

作ったもの

AndroidでいうViewPager的なUIのiOSバージョンです。

gif

ソース

構成

大体4つのパーツから成り立っています

  • MenuView... 各ページのタイトルを表示したり、UIPageControlを保持しているview
  • ContentScrollView... 各ページのコンテンツとなるViewを貼り付けるUIScrollView
  • SimpleViewController... 上記2つのViewを管理するViewController
  • Main.storyboard... SimpleViewControllerのViewをレイアウトするStoryboard

制約事項

  • 一応横画面にも対応
  • デプロイメントターゲットはiOS 7.0
  • できるだけシンプルにしてみたので、カスタマイズしやすいのではないかと...(リファクタ大歓迎)

仕組み的な話

メニューの同期部分

  • MenuViewとSimpleViewControllerのUIScrollViewDelegateでスクロールイベントを検知し、お互いに通知しあっています。
  • MenuViewのタップイベントに関しては、UITapGestureRecognizerを追加しました。
  • ページ管理はMenuViewのUIPageControlを共同で利用しています。

autolayout部分

  • MenuViewとContentScrollViewのlayoutは、Main.storyboardに任せます
  • 各ページのレイアウトは、各ページのStoryboard(ここではSome.Storyboard)に任せます
  • UIScrollViewに貼り付けているViewは viewDidLayoutSubviews()や、layoutSubviews()で都度調整しています

ちょっとトリッキー?な部分

各ページのタイトルを貼り付けるUIScrollViewのframeはgifアニメで見ると、濃い灰色になっている部分です。つまり通常はこの濃い灰色部分しかスクロールイベントが発生しないのですが、今回は、MenuView全体でイベントを発生させたかったので、下記のようにhitTestをオーバーライドして、MenuView内であればどこでもスクロールイベントが発生するようにしました。

MenuView.swift
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
    // Check if within own bounds. If YES, pass touch event to scrollView
    if self.pointInside(point, withEvent: event) {
        return self.scrollView
    }
    return nil
}

使い方

1. いわゆるモデルになるMenuElemを用意します。

MenuElem(name: "menu1", className: "SomeViewController", sbName: "Some")
  • name... ページのタイトル文字列です
  • className... ページのviewを管理するViewControllerのサブクラス名です
  • sbName... 上記ViewControllerと対応するStoryboardです

2. MenuElemを適切な順番に並べます

(配列のindexが若いほうが左から並びます)

SimpleViewController.swift
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    let menus: Array<MenuElem> =
    [
        MenuElem(name: "menu1", className: "SomeViewController", sbName: "Some"),
        MenuElem(name: "menu2", className: "SomeViewController", sbName: "Some"),
        MenuElem(name: "menu3", className: "SomeViewController", sbName: "Some"),
        MenuElem(name: "menu4", className: "SomeViewController", sbName: "Some"),
        MenuElem(name: "menu5", className: "SomeViewController", sbName: "Some"),
        MenuElem(name: "menu6", className: "SomeViewController", sbName: "Some"),
    ]

    self.menuView.setup(menus, delegate:self)
    self.setupContentScrollView(menus)
}

3. 必要に応じて各ViewControllerにパラメータを渡します

(ここでは、暫定的にextensionで渡していますが、きれいな渡し方を募集中です。)

SimpleViewController.swift
func setupContentScrollView(menus: Array<MenuElem>) {

    // http://app.coolors.co/e0acd5-3993dd-29e7cd-6a3e37-c7f0bd
    let colorPalette = [0xE0ACD5, 0x3993DD, 0x29E7CD, 0x6A3E37, 0xC7F0BD]
    var cnt = 0
    for menu in menus {
        let sb: UIStoryboard = UIStoryboard(name: menu.storyBoardName(), bundle: nil)
        let vc = sb.instantiateInitialViewController() as UIViewController
        vc.menu = menu
        vc.view.backgroundColor = UIColor(netHex: colorPalette[cnt%colorPalette.count])
        self.contentScrollView.addSubview(vc.view)
        self.vcs.append(vc)
        cnt++
    }
}

感想

最初は、UIScrollViewの中身に貼り付けるViewをどうやって再レイアウトさせよう...と思ってVisual Format Languageを使ってやっていたんですが、結局

  • UIScrollViewに貼り付けているViewは viewDidLayoutSubviews()や、layoutSubviews()で都度調整しています

とふうにできることがわかり、そっちにしました。。ソースからは削ったんですが、だいぶ勉強になったのでそこは別の機械に書いてみようかなと思います。

あと、このサンプルにTableViewとかcollectionViewとか入れて試してみたい。

最後に

改善大歓迎です!

参考

Swiftで既存のクラスに新たにプロパティを追加する方法

uacaps/PageMenu (今回のサンプルのもっと凄いversion)

41
42
0

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
41
42