By

UITableViewの上にUIButtonをスクロールせず固定して表示する方法!

Apps

こんにちわ! Railsエンジニアの @hbd225 です。
最近は仕事のあとswift書いてたりですので、swiftネタです!

初めてAirbnbのアプリを見たときは細かいところまでUIが作り込まれててすごいなーと感動したものですが、Airbnbの検索アイコンやメルカリの出品ボタンなど、UITableViewの上にボタンがスクロールされず固定されて表示されてる感じの素敵なUIをよく見かけますので、実装方法について調べてみました!


先に結論

UITableView のセル上に直接ボタンを追加しても、スクロールすると画面外に消えてしまいますので、ContainerView を配置して Embed segue で UITableView を繋いで実装することで、ContainerView の上に固定されてスクロールしない UIButton を追加することができるようになります。

画面のスクロールに合わせて UIButton の表示/非表示を切り替える際には、プロトコルを定義して UITableView のスクロールイベントを ContainerView 側の ViewController に通知する実装にしてみてます!


プロジェクト作成

今回は SingleViewApplication を使ってプロジェクト作成しますー。 Storyboard1

Storyboard から ContainerView を ViewController に追加

ContainerView は他の ViewController からも再利用が可能なViewを作るときに重宝するViewで、 こちらの記事 が分かりやすいです! Storyboard2

UITableView を実装

ContainerView を追加すると自動的に ViewController も追加されて繋がれていますが、そちらは一旦削除して、UITableView を好きなように実装し、Embed segue で繋ぎます。 Storyboard3

UIButton を 追加

Storyboard から ContainerView の上に UIButton を追加し、Constraints を設定してボタンの位置など調整します。 Storyboard4

実機で見てみる

UIButton がスクロールしても固定されて表示されました!
Device

スクロールの際に UIButton の表示/非表示を切り替える

スクロールダウン時はユーザーはコンテンツを見たいと思いますので UIButton を非表示にし、
スクロールアップの時に UIButton を表示するようにしましょう!

まずは UITableView 側の ViewController にプロトコルを定義して delegate 変数を用意します。

// スクロールの発生を検知してUIButtonの表示を切り替えるためのプロトコル
protocol ArticleViewControllerDelegate: class {
    func viewDidscrolledUp()
    func viewDidscrolledDown()
}

class ArticleViewController: UIViewController {

    weak var delegate: ArticleViewControllerDelegate?
    // スクロールの開始点
    var scrollBeginingPoint: CGPoint!

    // ドラッグしてスクロールが発生する直前に呼び出される
    func scrollViewWillBeginDragging(scrollView: UIScrollView) {
        scrollBeginingPoint = scrollView.contentOffset
    }

    // スクロールが発生している間常に呼び出される
    func scrollViewDidScroll(scrollView: UIScrollView) {
        let currentPoint = scrollView.contentOffset

        if scrollBeginingPoint.y < currentPoint.y {
            print("scroll to bottom")
            delegate?.viewDidscrolledDown()
        } else {
            print("scroll to top")
            delegate?.viewDidscrolledUp()
        }
    }

    // 以下にUITableViewのための実装などなど...
}

extension ArticleViewController: UITableViewDelegate {
}

extension ArticleViewController: UITableViewDataSource {
}

続いて ContainerView 側の ViewController にプロトコルを実装します。

class ViewController: UIViewController {
    // UIButton の表示/非表示を管理
    private var isButtonDisplayed: Bool = true

    @IBOutlet weak var addPictureButton: UIButton!

    // 先ほど実装した UITableView 側の ViewController の delegate に self を設定
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "ArticleViewControllerSegue" {
            let vc = segue.destinationViewController as! ArticleViewController
            vc.delegate = self
        }
    }

    // 以下にviewDidLoad などの処理...
}

// スクロール発生時に通知されるプロトコルを実装
extension ViewController: ArticleViewControllerDelegate {
    // スクロールアップ時に UIButton を表示する
    func viewDidscrolledUp() {
        if !isButtonDisplayed {
            addPictureButton.hidden = false
            isButtonDisplayed = true
        }
    }

    // スクロールダウン時に UIButton を非表示にする
    func viewDidscrolledDown() {
        if isButtonDisplayed {
            addPictureButton.hidden = true
            isButtonDisplayed = false
        }
    }
}

もう一度実機で見てみる

スクロールの発生によって UIButton の表示/非表示が切り替わりました!
Spring を使ってきれいにアニメーションをつけてあげるとなお良しですね! Device

エンジニア募集中!

ビジネスバンクグループではエンジニアを募集中しています。

弊社が採用しているテクノロジや開発環境に興味を持った方は、 ここから是非エントリー を!