タイトルが???になってしまいました( ・ὢ・ )
PageViewControllerで移動した際に上部に作成したタブを少しずつPage移動と比例させて動かしたいを今回のテーマとして作成しました。
これまでに参考にさせていただいたもの書いておきます。
iPhoneアプリでUIを作るためのTipsとContainerView・UIPageViewControllerを使ったサンプル紹介
【Swift 3】UIPageViewControllerとUINavigationControllerを使ってスワイプで画面切り替えをしよう
使うと手放せなくなるSwift Extension集 (Swift3版)
参考にしたかもしれないっと思ったものはまた都度あげて行きます
で今回作成したものです。
ソースなどみたい場合は以下のgithubからよろしくお願いします。むしろ見てもらう程で書いてます。
https://github.com/sachiko-kame/UITabSample
で、私はこれを作成する前に困っていたこと
- アニメーションでのタブ移動だとカクカクなるし時には左、右に余白ができる
- 少しずつページ移動した際に上のタブも少しずつ動かしたい
で、なんでこの記事を書いたか
- 似たものはあったのですが、上がCollectionViewでってのがなかなか見つからなかった。あとライブラリだったりした
- この実装を説明するためには簡単にこれだけになるべく焦点を当てたものを作成したかった。
- 作ったらqiitaに絶対書きたくなる。そしてqiitaに書くって尊敬する先輩に約束したのです(◍•ᴗ•◍)
では早速書きます!よろしくお願いします
まず、要点などをいう前に先に主たるファイルのソースを見てください!
import UIKit
class ViewController: UIPageViewController,UIPageViewControllerDataSource,UIPageViewControllerDelegate,UICollectionViewDelegateFlowLayout{
let pagelist = ["PAGE0", "PAGE1", "PAGE2", "PAGE3", "PAGE4", "PAGE5", "PAGE6", "PAGE7", "PAGE8", "PAGE9"]
var pageControllergrop = [UIViewController]()
var collectionView:UICollectionView!
let labeheight:CGFloat = 60
var page:Int = 0
init(){
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.makeViewcontoller()
self.setViewControllers([pageControllergrop.first!], direction: .forward, animated: false, completion: nil)
// collectionViewレイアウト作成
CollectionViewLayoutSet()
// collectionViewその他設定
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
collectionView.delaysContentTouches = false
//デリゲートの設定(PageViewControllerとUICollectionView)
dataSource = self
delegate = self
collectionView.dataSource = self
collectionView.delegate = self
view.addSubview(collectionView)
self.view.subviews
.filter{ $0.isKind(of: UIScrollView.self) }
.forEach{($0 as! UIScrollView).delegate = self }
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func CollectionViewLayoutSet(){
let flowLayout = UICollectionViewFlowLayout()
flowLayout.minimumInteritemSpacing = 0.0
flowLayout.minimumLineSpacing = 0.0
flowLayout.scrollDirection = .horizontal
flowLayout.itemSize = CGSize(width:viewframewidth / 3 , height:CGFloat(labeheight))
let rec = CGRect(x: 0.0, y:statusBarHeight , width:viewframewidth , height: labeheight)
collectionView = UICollectionView(frame: rec, collectionViewLayout: flowLayout)
}
func makeViewcontoller(){
for i in 0...(pagelist.count - 1){
let viewController:SampleViewController = SampleViewController()
viewController.Set(Text:pagelist[i])
self.pageControllergrop.append(viewController)
}
}
}
extension ViewController{
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let index:Int = pageControllergrop.index(of: viewController)!
page = index
view.viewWithTag(index + 1)?.backgroundColor = UIColor.gray
//1ページ何もしない
switch index {
case 0:
return nil
default:
//2だったら1に、 3だったら2に
return pageControllergrop[index-1]
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let index:Int = pageControllergrop.index(of: viewController)!
page = index
view.viewWithTag(index + 1)?.backgroundColor = UIColor.gray
switch index {
//最終ページ何もしない
case pageControllergrop.count-1:
return nil
default:
//最終ページでない場合進める
return pageControllergrop[index+1]
}
}
}
extension ViewController:UICollectionViewDataSource, UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pagelist.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
collectionView.register(cellType: SampleCollectionViewCell.self)
let cell:SampleCollectionViewCell = collectionView.dequeueReusableCell(with: SampleCollectionViewCell.self, for: indexPath)
cell.Set(Text:pagelist[indexPath.row])
return cell
}
// Cell が選択された場合
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//選択された所に遷移
self.setViewControllers([pageControllergrop[indexPath.row]], direction: .forward, animated: false, completion: nil)
}
}
extension ViewController{
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if(scrollView.className == "UICollectionView"){
return
}
let LeftRightJug = viewframewidth - scrollView.contentOffset.x
if(LeftRightJug == 0){
return
}
let Move = scrollView.contentOffset.x / 3
let first:Int = LeftRightJug > 0 ? 1 : 0
let End:Int = LeftRightJug > 0 ? (pagelist.count - 1) : (pagelist.count - 2)
if(first < page && page < End){
let pageWidth = CGFloat(page - 2) * viewframewidth //タブ真ん中に
let TabPageWidth = pageWidth / 3
collectionView.contentOffset = CGPoint(x:Move + TabPageWidth, y:0)
}
}
}
このほかに作成したものはgithubで確認していただけると嬉しいです。このほかで作成したファイルの種類のみ書きます。
・CollectionViewで使うためのUICollectionViewExtensionファイル
・CollectionViewの名前設定の時に使うためのクラスの名前取得NSObjectExtensionファイル
・UICollectionViewCellファイル
・ページビューコントローラーで使うビューコントローラーのファイル
で、メインのファイルで何を行っているかを書きます。
- UIPageViewControllerを継承させてクラス自体PageViewControllerにしている。
- CollectionViewの作成を行っている。
- CollectionViewとUIPageViewControllerとUIPageViewControllerのSubViewにあたるScrollViewにdelegateをセットしている
- ページ移動ができるようにPageViewControllerのdelegateに記述
- CollectionViewのdelegateにはCollectionViewCell作成とタッチ(タッチされたところのView表示が行われるようになってる)
- ページ移動(少しずつ)の際にスクロール量をscrollViewDidScrollで取得してCollectionViewのcontentOffsetの位置の調整を行っています 今回の肝になる部分です!
実装の際の注意点
- CollectionViewのcontentOffsetですがcontentOffset.xとできればやりたいのですが、なぜかうまく行かないとどこかに書いてあったためcontentOffset.xでなくcontentOffsetの設定を行っています。ちなみにcontentOffset.yは0で設定してあります。
if(scrollView.className == "UICollectionView"){
return
}
- 上記のところで?と思ったかたもいると思いますが、このデリゲートメソッドではCollectionViewのスクロール検知とPageViewControllerのSubViewのScrollの検知が入っています。もし両方に同じ処理を行ってしまったらPageViewContorollerのScrollView動かして、CollectionView動かしてと適応がうまく行かなくなることもありますし、そもそもCollectionViewのスクロールに今回の処理を入れたくないためreturnで返しています。
最後に
シンプルでありますが、以上で説明を終了させていただきます
見ていただきありがとうございました!シャキッ!