TabPageViewControllerの凄いと思ったところを紹介していきます

#はじめに
無限スクロールの実装をおこなおうと思った方なら誰でもが知っているであろう…

**TabPageViewController**
githubリンク→[EndouMari/TabPageViewController](https://github.com/EndouMari/TabPageViewController)

そしてこのライブラリのコードを見ていたら、
え！そこまで気を遣っていたの！
このライブラリ！ってすごすぎるのでは！！
ってかここまで挙動に気を遣っているのこれだけでは…？？？
などなど思ったので、
今回はこのライブラリで私が凄いと思ったことなどを紹介していこうと思います。
![fec3a9fdc40e773daf64903de9102c2d_s.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133940/35f46d78-ec4a-527c-979a-83fa7bf7f0d6.jpeg)
『なぜ犬の写真？』って思った方いると思います。
理由はただ犬の写真を使いたかっただけです！(⭕️｡･ω･｡⭕️)

⭐️追記⭐️
似たようなライブラリないかな？って方に以下！！
まだ使ってないのですがあまり知られていないのではとここで共有してみました。
誰か使いましたら是非記事など書いてアップしてください(✿´ ꒳ ` )期待！！
https://github.com/tamanyan/SwiftPageMenu


注意:
このライブラリの凄いと思うところの挙動を紹介するのに自分なりにSampleCodeを書いたりもします。
あくまで挙動のSampleなので必ずしもライブラリと同じ記述ではないことご了承ください。

お願い:
違うと思うところ、記事などに意見などありましたら教えていただけると嬉しいです。
その際は優しくしてくれるともっともっともっと嬉しいです。🐥

sampleCode場所:
注:以下メモ用もかねているので…すみません、消したつもりないのに消してしまったみたいです、
整理の時に間違って消しちゃったみたいです、ごめんなさい、、、
https\://github.com/sachiko-kame/qiitaSample

#この記事を書く目的
『このライブラリってこんなに気を遣っていたんだ！すごい！』と思う人を増やす。

#どんなものかライブラリのsampleにて…
<img width="200" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/133940/6e9eff81-01fb-0191-1f7f-fce449b5a217.gif">


#無限スクロールの実装

👩まず無限スクロールできるライブラリが少ないのでこの無限スクロールができるこのライブラリは凄いと思いました。

↓どのような実装だったかまとめてみました。

- **UIPageViewControllerのpageを適切なもので返す。**

まずこのライブラリは無限スクロールを行うにあたり、アイテム数×3のUICollectionViewCellがあります。
しかし、PageViewControllerのpage数はアイテム数しかありません。
っとするとアイテムが3つの場合、0,1,2までならそのままIndexを返せばいいのですが、3,4,5,6,7,8とindexが返ってきた時はどのようにして返せばいいのか？
3,4,5,6,7,8からどのようにしてUIPageViewControllerのPage番号を返せばいいのか！！

でそれを行っているcodeがここの部分

```.swift
/*
isInfinityがtrue(無限スクロールの設定)なら、UICollectionViewCellのindex.itemのアイテム数で割った時のあまりの数、そうでなければそのままindexPath.itemをfixedIndexとする
*/
let fixedIndex = isInfinity ? indexPath.item % pageTabItemsCount : indexPath.item
```

　ここでは無限スクロール部分が欲しいので(item部分もrowに変更)

```.swift
let fixedIndex = indexPath.row % pageTabItemsCount

/*
item数を3とした時のそれぞれの出力
fixedIndex = 0 % 3 //0
fixedIndex = 1 % 3 //1
fixedIndex = 2 % 3 //2
fixedIndex = 3 % 3 //0
fixedIndex = 4 % 3 //1
fixedIndex = 5 % 3 //2
*/
```
あとはこの数のPageViewControllerのPageを返すだけです！！
cellタップ時などにこの計算をしてそれに適したpageを返せばPageViewController自体の無限スクロールは完成となります。

- **UICollectionViewでの適切なoffset位置変更**

```.swift
extension infinityScrollViewController:UICollectionViewDelegate{
    func scrollViewDidScroll(_ scrollView: UIScrollView){
        let scrollViewContentOffsetX:CGFloat = scrollView.contentOffset.x
        let pageTabItemsWidth:CGFloat = (self.collectionView.contentSize.width / 3)
        
        if scrollViewContentOffsetX <= 0.0 {
            self.collectionView.contentOffset.x = pageTabItemsWidth
        }else if(scrollViewContentOffsetX > pageTabItemsWidth * 2){
            self.collectionView.contentOffset.x = pageTabItemsWidth
        }
    }
}
```

pageTabItemsWidthはCollectionViewはアイテム数を3倍にしていない場合のcollectionViewのcontentSize幅を求めています。

その下の部分では最初の表示よりも前に行った場合全く同じ表示になるようにcontentOffsetを移動、その後もほぼ同じようなことです。

っというのを動画で作成してみました。行っていることはこのような感じでこれを瞬時に行い移動している感を無くしています。

![infinitySample.gif](https://qiita-image-store.s3.amazonaws.com/0/133940/b0c70b59-e7bb-d4e5-e2c1-9540c82ddd8d.gif)

ちなみにこの感じでのシュミレーターの見え方はというとこんな感じです。
<img width="200" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/133940/adc5a76c-6ef1-2fed-d302-9739fa13db2b.gif">



#少しずつのスクロールでハイライトの挙動を変える
👩pageが遷移した時にハイライトの調整はよくあると思いましたが、以下のgifのように少しずつのスクロールによっての調整をおこなっているの凄いなと思いました。
<img width="200" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/133940/20770f22-7269-f459-8a59-11186b2ec725.gif">

↓どのような実装だったかまとめてみました。

- **PageViewControllerのスクロール量を監視して調整する**

の部分でpointだと思うところを抜粋してみました。

```.swift
let scrollOffsetX = scrollView.contentOffset.x - view.frame.width
tabView.scrollCurrentBarView(index, contentOffsetX: scrollOffsetX)

```

```.swift
let scrollRate = contentOffsetX / frame.width

if fabs(scrollRate) > 0.6 {
    nextCell.highlightTitle()
    currentCell.unHighlightTitle()
} else {
     nextCell.unHighlightTitle()
     currentCell.highlightTitle()
}
```
コードのニュアンスとしてはスクロール量で今のcellと次のcellを調整するといった感じです。
しかしこれは本当にうまくいくのか？
っということで同じことをしたlog出力を。
![v.gif](https://qiita-image-store.s3.amazonaws.com/0/133940/a4073d5f-cece-2b62-eecf-cd837a2e8b6f.gif)

半分のとことで0.5になっていることが確認できます。
っということはスクロール量に応じて調整すればできるということがわかります。

ちなみにPageViewControllerの前のページから次のページにいくまでは
0　-　view.frame.width　-　view.frame.width*2　
とう私の認識
ということは、

```.swift
let scrollOffsetX = scrollView.contentOffset.x - view.frame.width
```
の部分ではまずは全体的なものをview.frame.widthの数に戻してあげているようです。

```.swift
let scrollRate = contentOffsetX / frame.width
```

の部分では一旦スクロール量をview.frame.width幅にしているので、それを0〜1あたりになるように計算しているようです。

あとは0.6以上、すなわち次のpageの幅の方が広ければ次のcellをハイライト、今のcellのハイライトを解除、逆も同じようにしている感じです。

これをスクロール変化ごとに行って実現しているみたいです。


#少しずつのスクロールで下スライドバーの挙動を変える
👩これも上と少し似ていますが、下のバーが少しずつのスクロールで調整されているところが凄いと思いました。しかもこれcell幅が違う時も考慮されているのが、さらに凄いと思いました。
<img width="200" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/133940/a9b95132-4208-8fc3-598e-597e2f1cccbb.gif">

↓どのような実装だったかまとめてみました。

- **PageViewControllerのスクロール量を監視して調整する**

の部分でpointだと思うところを抜粋してみました。

```.swift
let distance = (currentCell.frame.width / 2.0) + (nextCell.frame.width / 2.0)
```

cell幅が違うのを調整するために、次のcellが真ん中にくるまでの量の算出です。
正直これを見た時、お〜こうやってやるのか！！なるほどー！！っとすごく驚きました！

あとはバーをこの数を使ってうまく調整してバーの幅調整を行っているみたいです。

ちなみにこれも、スクロール変化したごとに行って実現しているみたいです。
結構処理が入っているのを見ながらやはりここまでしないとこの実装は難しいんだなっとすごくすごく思っていました。

#下スライドバーの挙動をどのように実現しているの？
👩CollectionViewをスクロールした時は、綺麗にバーが乗った状態でスクロールされていて、PageViewControllerのスクロールをしている時はバーの長さが綺麗に調整されているのが凄いと思いました。
<img width="200" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/133940/a9b95132-4208-8fc3-598e-597e2f1cccbb.gif">

<img width="200" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/133940/3860027f-0880-619e-227f-3d6c03b65abd.gif">

↓どのような実装だったかまとめてみました。

これは真ん中に置くバーとcell一つ一つに置くバーがあり、
それを各スクロール始まった時スクロールしているときにhiddenでの調節を行っているといった感じでした。

PageViewControllerがスクロールされているときは真ん中のバーを表示、調整、
CollectionViewがスクロールされているときは、真ん中のバーを非表示、cellのバーを表示。

ここで、でも現在のcellのバーをスクロール時に表示しとくのは？っと思われる方もいると思います。
それは現在表示しなければいけないcellを保持しといてそれがきたらそのcellにしそうでない場合は通常のcellにするといった感じです。
↑
cellが表示されるときに通るデリゲートメソッドでの。

イメージを表示するのならこのようなものがありそれを調整するといった感じでした。

<img width="200" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/133940/b652b077-0606-738e-169c-506b444aca4d.png">


#スライド時のあれはそうやって解消していたのか！！
👩スクロール時に引っかかりがない。

PageViewControllerのスクロール時、引っかかりをなくすために同期処理、または遅延処理を入れていたような記憶でしたがさだかでないのここはメモ程度にさせてください。

結構有名どころでも、こういう実装で引っかかりがあるとこがあると思っています。
そういう挙動を確認するたびに、このライブラリはそこまで気を遣っているのか！すごいなっと思っていました！！

#最後に
とりあえずこのライブラリはとても気を遣っているすごいライブラリだと思いました。
この記事をみて、『このライブラリってそこまで気を遣っていたの！！！』って思ってくれた方が一人でもいたら記事投稿は成功です！。