2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

インスタグラムのマイページのようなViewを簡単に作れるライブラリを公開しました

Last updated at Posted at 2021-03-03

概要

インスタグラム,Tiktok,Twitterのマイページで見るようなページを実装してOSSとして公開しました。

# TopContentPager とは? 上記のような複雑なページを簡単に作ることができるフレームワークです。 上記のようなページの特徴としては以下。
1. 中断にページのタブがあり、上にスクロールすることで上部に固定される
2. 全てのページで共通した縦スクロールが可能なヘッダーが存在する
3. 上にスクロールしてページタブが固定されている状態でページを切り替えると各スクロール量は維持されているが、1つのページで上部まで(ページタブが固定されない部分まで)スクロールした状態でページを切り替えると全てスクロールはトップまで戻ってきている

これらの複雑なロジックを考えることなく、ページの実装を実現できます。

使い方

導入

まずはライブラリをinstallしてください。
Podfileに下記を追加して pod install をすればinstall完了です。

Podfile
pod 'TopContentPager'

実装

使うものは

  • ContentTableBody
  • TopContentView
  • TopContentPagerViewController

のみです。

親ViewControllerの作成

まず、大元となる親ViewControllerを実装します。
TopContentPagerViewController クラスを継承した ParentViewController を実装します。
ParentViewController には TopContentPagerDataSource を設定してください。
DataSourceには下記の二つの関数があります

func topContentPagerViewControllerTopContentView(_ viewController: TopContentPagerViewController) -> TopContentView
func topContentPagerViewControllerViewControllers(_ viewController: TopContentPagerViewController) -> [ContentTableBody]

それぞれ

  • TopContentView を継承した共通のヘッダー部分になるView
  • ContentTableBody プロトコルの適応した各ページのViewになるViewControllerの配列

を返します。

上記の関数を設定したら、下記の関数でdataSourceをselfに設定してください。

func setupWillLoadDataSource()

これで以下のようになり、最低限の親ViewControllerの実装は完了です。

ParentViewController
final class ParentViewController: TopContentPagerViewController {
    override func setupWillLoadDataSource() {
        super.setupWillLoadDataSource()
        dataSource = self
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

extension ParentViewController: TopContentPagerDataSource {
    func topContentPagerViewControllerTopContentView(_ viewController: TopContentPagerViewController) -> TopContentView {
        // return common HeaderView in all pages.
        TopView()
    }
    
    func topContentPagerViewControllerViewControllers(_ viewController: TopContentPagerViewController) -> [ContentTableBody] {
        // return ViewControllers for each page.
        [Page1ViewController(), Page2ViewController(), Page3ViewController()]
    }
}

共通のヘッダーViewの作成

次に共通となるヘッダーViewを作成します。
TopContentView を継承したクラス TopView を実装します。

TopView
final class TopView: TopContentView { }

はい、これだけです。あとはいつも通り普通にViewを作ってください。xibにしてもコードにしてもAutoLayoutを使うことを推奨します。

各ページのViewControllerの作成

最後にそれぞれのページとなるViewControllerを作っていきます。
ContentTableBody を継承させたViewControllerを実装します。
ContentTableBody の定義は以下です。

ContentTableBody
public protocol ContentTableBody: UIViewController {
    var pagerItem: PagerItem { get }
    var scrollView: UIScrollView! { get }
    // 〜省略
}

pagerItem はページのタブ部分のデザインを設定するものです。
.text .image .textAndImage .custom があり好きなものを設定してください。
詳細はここにあります。
-> https://github.com/itsukiss/TopContentPager#pageritem

scrollView にはコンテンツとなるViewを設定してください、基本的には tableViewcollectionView です。

下記が最低限設定した時の実装です。

Page1ViewController
class Page1ViewController: UIViewController, ContentTableBody {
    
    var pagerItem: PagerItem = .text(.init(title: "ページ1"))
    var scrollView: UIScrollView!
    @IBOutlet weak var tableView: UITableView! {
        didSet {
            scrollView = tableView
        }
    }
}

完成

これだけで上記のような共通のヘッダーがあるPagerを作ることができます。
上記の実装では必要最低限の説明しかしていませんので、READMEサンプルプログラムを見ることをお勧めします。
特にサンプルプログラムはわかりやすく作ってますのでぜひ参考にしてください。

設計

View構造は下記のようになっていて、基本的には下記のような実装をしています。
(READMeにもかいてますが、 ContentTopProtocol はもはや使われていません。 TopContentView に置き換えて読んでください)
また、各スクロール時のViewの挙動はGifのアニメーションで表しているので、みていただけるとイメージがわくと思います。

  • 横スクロール時に一番最前面のEscapeViewにTopContentViewを貼り付ける。
  • 縦スクロール時にはContentTopCellに貼り付けることで縦スクロールのジェスチャも可能に
  • ある一定以上までスクロールしたら、TopContentViewをEscapeViewに貼り付けページタブ部分だけをView内にみえるように
  • TopContentViewではhitTestをoverrideして判定し、横方向のスクロールを制御しています。
  • どこかのページの縦スクロールがTopContentViewまで到達したとき(つまり上部固定のヘッダーが表示された時)に全てのページのcontentOffsetを合わせるように
    • これはことばにするとややこしいですが、上部コンテンツが共通の場合上部コンテンツが見えてる場合に横スクロールすると他のページのコンテンツがcontentOffsetを保持していると、TopContentViewに食い込むような形になるからです。
    • 下のgifアニメーションの最後の「スクロールがどちらも下の方まで行われていた時を見ればわかります。

1.View構造/TopContentView内の横スクロール制御
TopContentPagerStructure.png
2.スクロール時のViewの挙動

ここでは細かく説明しませんが、もし気になる方はコードを読んでみてください。

終わりに

今回はぱっと見て簡単にできそうだけど、やってみたら結構複雑なインスタグラムやtiktokのマイページのようなPagerを作りました。
困っている人の助けになればと思います。
もし何か質問や意見があれば、ガンガンください。

リンク集

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?