こんばんは!
セプテーニ・オリジナルの寺坂です。
現在、エンジニア一年目。
勉強の日々でございます。
いまの仕事で「iOS開発」に携わっているので、
今回は、Swiftネタを書きます。
つたない所があるかもしれませんが、
温かい目で最後までご一読いただけると嬉しいです!
それでは、よろしくお願いします。
スワイプ移動ページUIの実現
ここでの スワイプ移動ページUI とは、
「あらかじめ複数のページを横に並べておいて、
スワイプによって横スクロール式にページを切り替える」
UIを指しています。
たとえば、
トップにナビゲーションバーやセグメントを固定しておいて、
その下のページをスワイプで切り替えたい時
などを想定してます。
横スクロール型UIと言ってもいいかもしれませんね。
このスワイプ移動ページUIを実現する方法はいくつかありますが、
PageViewとかScrollViewを使うと、実装の手間がかかりがちです。
そこで、今回はより簡単な方法を模索して、
UICollectionViewを使ったスワイプ移動ページUI
を実現したいと思います。
##1. UICollectionViewControllerを継承させる
まず始めに、Storyboard上にUICollectionViewControllerを追加して、
コントローラーとひもづけましょう。
UICollectionView自体の解説は本筋から外れるので、
詳細は割愛してざっくり説明で進みます。
手順
-
StoryboardにCollectionViewを追加
-
Layoutは「Flow」
-
Scroll Directionは「Horizontal」
-
Scrollingの「Paging Enabled」にチェック
-
UICollectionViewControllerを継承したPageCollectionViewControllerを作成
-
StoryboardのCollectionViewのコントローラーにPageCollectionViewControllerを指定
実装
import UIKit
class PageCollectionViewController: UICollectionViewController {
var pages:Pages = Pages(){
didSet {
self.collectionView?.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
##2. データの準備とUICollectionViewDelegateの実装
最低限、セルの数とセルの種類を返すdelegateは実装しましょう。
手順
- 構造体を定義
- 組み込みたいページを追加
- "PageCollectionViewCell"というIDでUICollectionViewCellを用意
- 構造体に定義されている数だけ、CollectionViewCellを作る
実装
import UIKit
struct Pages {
var viewControllers:[UIViewController] = []
}
class PageCollectionViewController: UICollectionViewController {
var pages:Pages = Pages(){
didSet {
self.collectionView?.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
let page1 = UIViewController()
page1.view.backgroundColor = UIColor.yellowColor()
self.pages.viewControllers.append(page1)
let page2 = UIViewController()
page2.view.backgroundColor = UIColor.blueColor()
self.pages.viewControllers.append(page2)
}
// MARK: - UICollectionViewDelegate
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.pages.viewControllers.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("PageCollectionViewCell", forIndexPath: indexPath) as UICollectionViewCell
let view = self.pages.viewControllers[indexPath.row].view
return cell
}
}
##3. セルにページをaddSubviewする
次の部分を編集して、
indexPathに対応するviewをセルに貼付けましょう。
手順
- indexPathに対応するviewを取り出す
- cell.contentViewにはり付ける
実装
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("PageCollectionViewCell", forIndexPath: indexPath) as UICollectionViewCell
let view = self.pages.viewControllers[indexPath.row].view
cell.contentView.addSubview(view)
return cell
}
}
##4.セルのサイズを動的に変える
ここまでの実装で、セルにページ情報を表示させることが出来ますが、
セルのサイズが小さいままです。
次は、セルサイズを画面全体の大きさに変動させましょう。
そのためには、作成したPageCollectionViewControllerに
UICollectionViewDelegateFlowLayoutのメソッドを実装します。
ここでは次のようにコードを追加します。
1つ目のメソッドで返すCGSizeがセルの大きさになり、
残りの2つは、それぞれ横のスペース・縦のスペースになります。
(後の2つはStoryboardからも設定可)
注意点
- 状況によってはAutoLayoutとの兼ね合いに注意が必要です
手順
- メソッド内で実現したいCGSizeを求めて返す
- セルとセルの間スペースは、0を指定
実装
/// セルの大きさ
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var pageViewRect = self.view.bounds
return CGSize(width: pageViewRect.width, height: pageViewRect.height)
}
/// 横のスペース
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0.0
}
/// 縦のスペース
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0.0
}
完成
以上です。
最後に完成したコードを載せておきます。
コードを整理してコンパクトにすると、ぴったり50行。
最後まで読んでくださり
ありがとうございました!
追記
2015/01/15 - コメントで指摘頂いた内容を反映し、下記を追加
self.collectionView?.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "PageCollectionViewCell")
実装
import UIKit
struct Pages {
var viewControllers:[UIViewController] = []
}
class PageCollectionViewController: UICollectionViewController {
var pages:Pages = Pages(){
didSet { self.collectionView?.reloadData() }
}
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView?.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "PageCollectionViewCell")
let page1 = UIViewController()
self.pages.viewControllers.append(page1)
let page2 = UIViewController()
page2.view.backgroundColor = UIColor.blueColor()
self.pages.viewControllers.append(page2)
}
// MARK: - UICollectionViewDelegate
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.pages.viewControllers.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("PageCollectionViewCell", forIndexPath: indexPath) as UICollectionViewCell
let view = self.pages.viewControllers[indexPath.row].view
cell.contentView.addSubview(view)
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var pageViewRect = self.view.bounds
return CGSize(width: pageViewRect.width, height: pageViewRect.height)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0.0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
return 0.0
}
}