Upload
fumiya-sakai
View
1.932
Download
3
Embed Size (px)
Citation preview
UIPageViewControllerとContainerViewで
こんな見た目を実現するTips
集まれSwift好き!Swift愛好会#6
2016/04/24FumiyaSakai
自己紹介と簡単な経歴など
✦ 今までの仕事履歴(本業)
石川県金沢市生まれ
本業はサーバーサイドのプログラマ※Rails&PHP使い
26歳〜31歳: Webプログラマ(PHP&Rubyがキャリア長い)
23歳〜25歳: Webデザイナー兼ディレクター
チャンスがあればiOSアプリ開発も絶賛やってみたい!
趣味:シルバーアクセサリー集め・スイーツ作り・アプリ開発
女子向け・グルメ・エンタメ関連のお仕事が多い
Qiita:http://qiita.com/fumiyasac@github
Github:https://github.com/fumiyasac
✦ 酒井文也(さかいふみや)
東京(大塚)住まいの31歳
こんな格好を普段からしているので遊び人に見られますがエンジニアです。
文系卒に思われますが実は数学科で理系卒です。
めっちゃお酒好きそうに見えますがビール苦手でお酒も超弱いです。
今でもたまにUIまわりとか触りたくなることがあったりなかったり
今年の4月からフリーランスです。(今は渋谷のとある会社にいます)
最近のはまっている食べ物はカボチャと担々麺です。
最近はSwift以外ではRailsやLaravel・CakePHP・Node.jsなんかも
これまでに作ったもの(ネイティブアプリ)
①簡易家計簿アプリ「Coffre」
②ゲームアプリ「10秒虫食い算」
・カレンダーを自作しています
・シンプルなお小遣い帳感覚で支出管理できます
・全問正解者ほとんどいません…
・不定期ですがコラムも書いています
・サーバーサイドはRubyonRailsを使用
http://www.coffre.me/
・デザインにもこだわってみました(特にグラフ)
・実はちょっとバグがあります。
・問題は今後追加予定(現在110問収録)
個人的にはなりますが、他にもアプリ・Webサービスなど開発中です(2016年も宜しくお願いします)
・サイト等は次回のアップデートで公開予定
http://blog.just1factory.net/services/284
・若干の中毒性を含みます
✦ ViewControllerをひとまとめにして扱ってくれる優れもの
UIPageViewControllerについての基礎知識
・アプリの最初のチュートリアル画面もよく用いられる表現方法
・ニュースメディアアプリやキュレーションアプリのタブと連動させて中を変えるUIにも使われる
親のViewControllerが子のViewController達を管理してくれるので、ViewControllerの責務を明確にできる
★それぞれの子のViewControllerの独立性を維持しながら切り替えることが可能
✦ ContainerViewを活用するとUIを作る幅が広がる場面も
ContainerViewについての基礎知識
多用禁物だけどViewControllerを重ねる場合や親のViewControllerの一部に別の部品を表示する場合に便利
★組み合わせ方でUIPageViewControllerみたいなものや重なりを表現することも可能
・UIScrollView+ContainerViewを使用してUIPageViewControllerのような動きももちろん可能
・隠れているメニューをメイン側のボタンから操作して表示させるようなUIの表現もできる
✦ PageViewControllerとContainerViewのサンプル2種類
今回のサンプルの概要
PageViewControllerはコードでレイアウト・ContainerViewのものはAutoLayoutとコード配置の合わせ技で
★どちらも似たような動きをしているものですが、実装の違いを見極めてみよう
・ViewController同士の重なりを考慮する場合はContainerViewを使うときれいにまとめられる
ハンバーガーボタンを押すと下に隠れているメニューの部分が表示
ページをめくるようなここは、(.PageCurl)で実現※デフォルト
UIScrollView+Containerでスクロールでコンテンツ切り替え
上のタブコンテンツに連動して動くラベルとコンテンツが連動
UIPageViewControllerでコンテンツ切り替え
・UIPageViewControllerはViewControllerの管理と切り替え
✦ 今回もサンプルと詳細な解説をご用意しました
サンプルはこちら
間違いやご指摘・もしくはもっとエレガントな実装のアイデアやご提案がありましたら是非ぜひお気軽に!
★サンプルはこちら
・組み方の手法やTipsに関しては色々手法があるので一つの解として捉えて頂ければ幸いです。
・UI系のライブラリに近しい動きをできるだけ強引に再現してみた形になります。
・Github:https://github.com/fumiyasac/CustomizeUISample
★詳細な解説はこちら
iPhoneアプリでUIを作るためのTipsとContainerView・UIPageViewControllerを使ったサンプル紹介
・Qiita:http://qiita.com/fumiyasac@github/items/1244abc8e3286c47ef50
★(参考)AutoLayoutを用いないで実装した例
メディア系アプリでよくあるUIを実現した簡易サンプル
・Qiita:http://qiita.com/fumiyasac@github/items/2490990be4c011935368
・Github:https://github.com/fumiyasac/mediaStyleContainer
✦ Storyboardに作成したViewControllerをひとまとめにする
PageViewControllerを使用したサンプル解説
PageViewControllerに入れるViewControllerを管理する際にはViewControllerのリストを追加する必要あり
★PageViewControllerで管理するViewControllerの一覧を取得する
・今回はUIPageViewControllerクラスを使う部分についてはコードで配置する方針
・各ViewControllerの”Identifier”をもとに追加対象のViewController達のリストを作成
//UIPageViewControllerに配置するUIViewControllerクラスの名称 static let pageControllerIdentifierList : [String] = [ "FirstViewController", "SecondViewController", "ThirdViewController", "FourthViewController", "FifthViewController", "SixthViewController" ] //UIPageViewControllerに追加するViewControllerのリストを生成する static func generateViewControllerList() -> [UIViewController] { var viewControllers : [UIViewController] = [] self.pageControllerIdentifierList.forEach { viewControllerName in //ViewControllerのIdentifierからViewControllerを作る let viewController = UIStoryboard(name: "Main", bundle: nil) . instantiateViewControllerWithIdentifier("\(viewControllerName)")
viewControllers.append(viewController) } return viewControllers }
子のViewControllerにIdentifierをつけておく
✦ UIPageViewControllerのインスタンスの初期化〜配置まで
PageViewControllerを使用したサンプル解説
UIPageViewControllerのインスタンス生成時とsetViewControllersが実装時の大きなポイントになる!
★ViewDidLoad内で行う処理の概要は初期化と読み込まれた際の表示に関する事など
・初期化→デリゲートの設定→初めに見せるViewControllerの決定→pageViewControllerの配置
・transitionStyleやnavigationOrientationで子のViewControllerの切り替え時の見た目を設定する
override func viewDidLoad() { super.viewDidLoad() //・・・(その他の副次的な部分は省略)・・・ //UIPageViewControllerの設定 // * .Scrollだと謎のキャッシュ問題が発生 self.pageViewController = UIPageViewController(transitionStyle: .PageCurl, navigationOrientation: .Horizontal, options: nil) //UIPageViewControllerのデリゲート self.pageViewController.delegate = self self.pageViewController.dataSource = self //UIPageViewControllerの初期の位置を決める self.pageViewController.setViewControllers([PageSettings.generateViewControllerList().first!], direction: .Forward, animated: false, completion: nil) //UIPageViewControllerを子のViewControllerとして登録 self.addChildViewController(self.pageViewController) //UIPageViewControllerを配置 self.view.addSubview(self.pageViewController.view) }
引数の意味:・第1引数:ページ送りする際のスタイル設定(.PageCurl:ページめくり/.Scroll:スクロール)・第2引数:ページ送りの方向設定(.Horizontal:水平方向/.Vertical:垂直方向)・第3引数:その他オプション設定
引数の意味:・第1引数:表示対象のViewController・第2引数:アニメーション方向(.Forwardor.Reverse)・第3引数:アニメーションの有無・第4引数:完了時の処理
✦ viewDidLayoutSubViews()内で大きさを再定義する
PageViewControllerを使用したサンプル解説
AutoLayout時はUIViewControllerやUIViewのライフサイクルを利用するor制約をコードで指定して配置
★UIViewControllerのライフサイクルを利用して位置を決めていく
・AutoLayout使用時はviewDidLoadorviewWillAppear内でCGRectMakeメソッド等で位置を決められない
・本当はAutoLayoutの制約をコードで配置する方法が望ましいのかもしれない・・・
//レイアウト処理が完了した際の処理 override func viewDidLayoutSubviews() { //UIScrollViewのサイズを変更する self.menuScrollView.frame = CGRectMake(
CGFloat(0), CGFloat(PageSettings.menuScrollViewY), CGFloat(self.view.frame.width), CGFloat(PageSettings.menuScrollViewH) ) //UIPageViewControllerのサイズを変更する //サイズの想定 →(X座標:0, Y座標:[UIScrollViewのY座標+高さ], 幅:[おおもとのViewの幅], 高さ:[おおもとのViewの高さ] - [UIScrollViewのY座標+高さ]) self.pageViewController.view.frame = CGRectMake( CGFloat(0), CGFloat(self.menuScrollView.frame.origin.y + self.menuScrollView.frame.height), CGFloat(self.view.frame.width), CGFloat(self.view.frame.height - (self.menuScrollView.frame.origin.y + self.menuScrollView.frame.height)) ) self.pageViewController.view.backgroundColor = UIColor.grayColor() self.menuScrollView.backgroundColor = UIColor.lightGrayColor() //UIScrollViewの初期設定 ※コード省略・・・ //UIScrollViewへのボタンの配置 ※コード省略・・・ //動くラベルの配置 ※コード省略・・・ }
・UIPageViewControllerのライフサイクルについて:http://qiita.com/mo_to_44/items/0ca628b4cc74c8c5599d
・UIViewのレイアウト周りの処理はviewDidLayoutSubViewsの内部でやった方がいいhttp://furodrive.com/2014/10/how_to_access_iboutlet/
✦ スワイプ操作で表示するViewControllerの切り替え
PageViewControllerを使用したサンプル解説
AutoLayout時はUIViewControllerやUIViewのライフサイクルを利用するor制約をコードで指定して配置
★UIPageViewDatasourceのメソッドを活用する
・前のページに送る→viewControllerBeforeViewController
・次のページに送る→viewControllerAfterViewController
//ページを次にめくった際に実行される処理 func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? { let targetViewControllers : [UIViewController] = PageSettings.generateViewControllerList() if self.viewControllerIndex == targetViewControllers.count - 1 { return nil } else { self.viewControllerIndex = self.viewControllerIndex + 1 } //スクロールビューとボタンを押されたボタンに応じて移動する self.moveToCurrentButtonScrollView(self.viewControllerIndex) self.moveToCurrentButtonLabel(self.viewControllerIndex) return targetViewControllers[self.viewControllerIndex] }
実装のポイント・メンバ変数viewControllerIndexの値をviewControllerAfterViewControllerは+1,viewControllerBeforeViewControllerは-1する・UIViewControllerのリストからメンバ変数viewControllerIndexの値のところのものを取得する・戻り値はUIViewController型になる
✦ ScrollViewで配置されているボタンでViewControllerの切り替え
PageViewControllerを使用したサンプル解説
ScrollViewとの連携も意外としやすい印象。PageViewControllerを活用して様々なUIの幅が広がると思う!
★ボタンがタップされたタイミングでsetViewControllersメソッドを呼び出す
・ScrollView内にボタンを設置するタイミングでタグを仕込みその値を活用する
・setViewControllersメソッドの第3引数をtrueにして切り替えるタイミングにページめくりをつける
//ボタンをタップした際に行われる処理 func buttonTapped(button: UIButton){ //押されたボタンのタグを取得 let page: Int = button.tag //UIPageViewControllerのから表示対象を決定する if self.viewControllerIndex != page { self.pageViewController.setViewControllers([PageSettings.generateViewControllerList()[page]], direction: .Forward, animated: true, completion: nil) self.viewControllerIndex = page //スクロールビューとボタンを押されたボタンに応じて移動する self.moveToCurrentButtonScrollView(page) self.moveToCurrentButtonLabel(page) } }
実装のポイント・ScrollView内にボタンを配置する際にタグを設定しておく・ボタンに設定したタグの値をもとに表示するViewControllerを決定する(setViewControllersでその処理を行っている)
animated:trueにして切り替えの際にアニメーション
✦ 土台となるViewControllerに2つのContainerViewを配置
ContainerViewを使用したサンプル解説
・重なり順は下から「メイン」→「メニュー」→「透明ボタン」となる
・viewDidLoad内で各々の要素に対する初期設定、viewDidLayoutSubviews内で要素の位置を再定義
それぞれのViewController同士を重ねて1つの画面内で表示したい場合にはContainerViewが活躍する!
★メニュー部分とメイン部分のContainerViewの配置と制約付けを行う
SideContentsController.swift
BaseContentsController.swift
✦ コンテンツ用のContainerViewにつながっているViewController
ContainerViewを使用したサンプル解説
・初期配置やAutoLayoutの制約に関しては上記の制約を参考にしてください
・大まかな枠組み部分はAutoLayout・ボタンの配置やScrollViewの設定についてはコードで実装
上記の方針が本当に正しいかは実際のところ自信がなかったりする…(ベストプラクティス的なものは?)
★メイン部分(BaseContentsController.swift)のパーツ配置と制約付けを行う
✦ 配置したScrollView内にContainerViewを配置する
ContainerViewを使用したサンプル解説
・AutoLayout+ScrollViewの組み合わせはハマりやすいポイントの典型例
・ContainerViewから「Control+ドラッグ」をして親のScrollViewにカーソルを当てEqualWidths(Heights)
上記の方針が本当に正しいかは実際のところ自信がなかったりする…(ベストプラクティス的なものは?)
★ScrollView内にコンテンツ用のContainerを配置する
SimulatedSizeをFreeformのwidth:3600
ここでライブコーディングします!
5分間お時間をくださいm(__)m
✦ 自力での実装に疲れそうなら進んで活用すべし!
UI系で気になるライブラリピックアップ
開発者の方々には本当に感謝しております!一同、素晴らしい開発者の皆様に礼!(おれもいつかは作る!)
★UIまわりのライブラリ4選
・RMPScrollingMenuBarController
https://github.com/recruit-mp/RMPScrollingMenuBarController
http://tech.vasily.jp/entry/tab_page_viewcontroller
・PagingMenuController
https://github.com/kitasuke/PagingMenuController
・PageMenu
https://github.com/HighBay/PageMenu
・TabPageViewController
https://github.com/EndouMari/TabPageViewController
<上記に関する紹介記事的なもの>
・UIPageViewControllerをつかって無限スクロールできるタブUIを実装してOSSとして公開しました
http://animane.hatenablog.com/entry/2015/09/27/133859
・SmartNewsのようなUIを実現するライブラリ3つを比較してみた
✦ 本にはなかなか載ってない部分だけど活用できると面白い!
今回のまとめ
ご清聴ありがとうございました!またこのような機会があった際には是非ともよろしくお願い致します!
★UIPageViewControllerやContainerViewを知ると気になるUI実装へ近づく
・自力での実装orライブラリ活用の選択に関する決め手の落とし所は引き続き研究中…
・UI系の優れたライブラリのコードを読むと実装のヒントやUIKitへの理解が深まるので面白いです
それぞれの持つ特徴や特性を公式リファレンス等で確認しておくと役に立つ
★AutoLayoutやUIViewControllerのライフサイクルに関しても確認
AutoLayoutでの制約のつけ方の理解やライフサイクルへの理解も重要ポイント
★はまりやすいレイアウトの事例についても事前に知っておく
ScorllViewとAutoLayoutの組み合わせ等の定番ものがあったりするので注意が必要
★UI系のライブラリの実装方法や中のコードについても見てみる
知っておくと役に立つ&汎用的なものも数多くあるのでチェックしておく