UITableViewのリサイズに合わせて一番下のセルを常に一番下に表示する方法

公開日: : iPhoneアプリ開発

20160807-113229.jpg

一番下のセルを表示している状態でUITableViewの下端を上に移動させると一番下のセルが隠れてしまいますが、隠れないように下端を移動させる方法を紹介したいと思います。

もし他に良い方法があればTwitterなどでぜひ教えて下さい。 ⇒ akio@東京アプリ開発講座8/13(@akio0911)さん | Twitter

    

スポンサード リンク

前提と下準備

今回は下記のような画面を作って検証を行いました。

UITableViewのサイズを変更するため、UITableViewControllerではなくUIViewControllerを使用し、UITableViewを1つ載せてあります。

20160807-141457.jpg

    

また、UITableViewの下端の制約に対して操作を行えるよう、以下のようにアウトレットを作成しておきます。

20160807-141743.jpg

    

方法その1 制約アニメーションだけ行う

まずは方法その1。Toggleボタンがタップされるたびに、UITableViewの下端の制約のconstantを変更してアニメーションさせます。

コードは以下のとおり。

@IBAction func pressToggleButton(sender: AnyObject) {
    animation1()
}

let tableViewResizeDy: CGFloat = 200.0

private func animation1() {
    if tableViewBottom.constant == 0.0 {
        tableViewBottom.constant = tableViewResizeDy
    } else {
        tableViewBottom.constant = 0
    }

    UIView.animateWithDuration(0.25) { [weak self] in
        guard let `self` = self else { return }
        self.view.layoutIfNeeded()
    }
}

    

しかしこの方法だと以下のように下の方のセルが隠れてしまいます。ちなみに薄緑の領域はUITableViewの下に敷いてあるViewです。

20160807-141016.jpg

なぜこうなるのかというと、UITableView自体のframe(覗き窓の大きさ)が変わっただけであり、contentOffset(覗き窓に表示したいコンテンツの左上の座標)が変わっていないからでしょう。だから一番上に表示されているセルはRow37のままになっています。

    

方法その2 制約アニメーションしながらscrollToRowAtIndexPath

次は方法その2。制約アニメーションしながらscrollToRowAtIndexPathでスクロールさせます。

コードは以下のとおり。

@IBAction func pressToggleButton(sender: AnyObject) {
    animation2()
}

let tableViewResizeDy: CGFloat = 200.0

/// 制約アニメーションさせながらscrollToRowAtIndexPath
private func animation2() {
    if tableViewBottom.constant == 0.0 {
        tableViewBottom.constant = tableViewResizeDy

        if let lastIndexPath = tableView.indexPathsForVisibleRows?.last {
            tableView.scrollToRowAtIndexPath(lastIndexPath, atScrollPosition: .Bottom, animated: true)
        }
    } else {
        tableViewBottom.constant = 0
    }

    UIView.animateWithDuration(0.25) { [weak self] in
        guard let `self` = self else { return }
        self.view.layoutIfNeeded()
    }
}

    

しかしこの方法でも同じ結果になってしまいます。指定したNSIndexPathを元に、scrollToRowAtIndexPathを呼び出した時点(=制約アニメーション前)のcontentOffsetを使ってスクロールしているからだと思われます。

20160807-141016.jpg

    

方法その3 制約アニメーションした後にscrollToRowAtIndexPath

制約アニメーション前に一番下のセルのNSIndexPathを保持しておき、制約アニメーション終了後にscrollToRowAtIndexPathを使って保持しておいたセルまでスクロールさせます。

コードは以下のとおり。

@IBAction func pressToggleButton(sender: AnyObject) {
    animation3()
}

let tableViewResizeDy: CGFloat = 200.0

/// 制約アニメーションした後にscrollToRowAtIndexPath
private func animation3() {
    if tableViewBottom.constant == 0.0 {
        tableViewBottom.constant = tableViewResizeDy
    } else {
        tableViewBottom.constant = 0
    }

    let lastIndexPath = self.tableView.indexPathsForVisibleRows?.last
    UIView.animateWithDuration(0.25, animations: { [weak self] in
        guard let `self` = self else { return }
        self.view.layoutIfNeeded()
    }, completion: {  [weak self] _ in
        guard let `self` = self else { return }
        if let lastIndexPath = lastIndexPath {
            self.tableView.scrollToRowAtIndexPath(lastIndexPath, atScrollPosition: .Bottom, animated: true)
        }
    })
}

    

最終的には目的の場所までスクロールしてくれますが、いったん下の方のセル群が隠れてしまうので、見た目としてはピョコッとした動作になってしまいます。

20160807-185811.jpg

    

方法その4 制約とcontentOffsetを同時にアニメーションさせる

UITableViewのサイズが変わっても一番下のセルが一番下に表示されていて欲しい = contentOffsetを制約アニメーション分だけずらさなければならないということであり、制約アニメーション終了時のcontentOffsetは制約アニメーション量を使って事前に計算できるので、制約とcontentOffsetを同時にアニメーションさせます。

コードは以下のとおり。

@IBAction func pressToggleButton(sender: AnyObject) {
    animation4()
}

let tableViewResizeDy: CGFloat = 200.0

private func animation4() {
    enum ContentOffsetAnimation {
        case On(dy: CGFloat)
        case Off
    }
    let contentOffsetAnimation: ContentOffsetAnimation

    if tableViewBottom.constant == 0.0 {
        tableViewBottom.constant = tableViewResizeDy
        contentOffsetAnimation = .On(dy: tableViewResizeDy)
    } else {
        tableViewBottom.constant = 0
        contentOffsetAnimation = .Off
    }

    UIView.animateWithDuration(0.25) { [weak self] in
        guard let `self` = self else { return }

        self.view.layoutIfNeeded()

        if case .On(let dy) = contentOffsetAnimation {
            self.tableView.contentOffset.y += dy
        }
    }
}

    

結果は以下のとおり。UITableViewの縮小時・拡大時の双方で、一番下のセルが常に一番下に表示されています。そして動きもスムーズです。

20160807-190641.jpg

20160807-190814.jpg

    

@akio0911はこう思った。

メッセージングアプリなどでスタンプ選択リストなどを表示する際に使える方法だと思います。

ぜひ活用してみてください!

この記事が気に入ったら「いいね!」しよう

follow us in feedly

Feedlyで最新記事を購読

Twitterで更新情報をゲット!

LINEでご感想・ご要望お送りください!
(スマホでLINEを起動 > 友だち追加 > QRコード)

関連記事

I20160417-171311.jpg

Xcodeのブレークポイントで変数の中身を通知センターに表示してみる

Xcodeのブレークポイントの機能を使って、ブレークポイントを通過した時点での変数の中身を通知セ

記事を読む

I20151107-111928.jpg

Swift 2で要求バージョンの指定・実行環境バージョンの確認を行う(#availableと@available)

Swift 2で追加された#available, @availableの使い方についてメモしてお

記事を読む

I20160124-111228.jpg

初心者がデザインを勉強するなら、まずこの本を読むといいかも

まず僕のスペックから話をすると、 アプリのコードを書いたり、ブログを書いたりしている

記事を読む

I20151103-131550.jpg

iPhoneアプリ開発にオススメの本7選(初心者向け含む)2015年11月版

Xcode 7/Swift 2/iOS 9に対応した関連書籍が揃ってきたので、iPhoneア

記事を読む

I20160525-233821.jpg

SwiftBondを使ってみた。observeやObservableの使い方など

Swiftバインディングフレームワークである「SwiftBond」を使ってみました!

記事を読む

I20160212-000722.jpg

Swiftにおけるセレクタ(Selector)について調べてみた

NSTimerなどを使う際、引数に「Selector」という型があるのですが、これがSwiftで

記事を読む

エディタのショートカット・Auto Layout・ブレークポイントに関する詳しい解説も!「Xcode5徹底解説」

著者の@es_kumagaiさんより献本御礼。iOSアプリ開発に用いるツール「Xcode 5」の

記事を読む

I20151119-165939.jpg

Swift 2.1に対応した本「詳解 Swift 改訂版」が出るぞ!

Swiftの文法に関する解説書の定番「詳解 Swift」の、Swift2.1に対応した改訂版「詳

記事を読む

20160724-154909.jpg

デザインの素人がノンデザイナーズ・デザインブックを読んだら、デザインの原則が結構分かるようになった!

ずっとエンジニアとして働いてきてデザインに関してはまったくの素人な僕ですが、「ノンデザイナーズ・

記事を読む

20140807-182914.jpg

【アプリ開発】iOS 8とiOS 7でUIAlertController・UIAlertView・UIActionSheetを切り替えて使う方法

@akio0911です。今までお世話になったUIAlertViewとUIActionShee

記事を読む

20170423-182541
良肌研究室のフェイスウォッシュとオールインワンジェルを使ってみた

株式会社ブラシナさんから、良肌研究室の商品をご提供頂いたので、

I20170228-002742.jpg
約3ヶ月で体脂肪率を6.2%も落とせたキッカケについて

(右上の赤枠内がダイエット開始前、左下の赤枠内が3ヶ月後の数値

I20161224-174949.jpg
「季節の野菜を直接配送!季節のスムージー」を買ってみた

「FiNCモール」で、「季節の野菜を直接配送!季節のスムージー」を

I20161002-152537.jpg
【メンズネイル】東京・新宿のネイルサロンでマットネイルしてもらった

2016年7月18日、東京・新宿のメンズOKなネイルサロン「Tot

I20160925-163452.jpg
タブバーアイコン非選択時の色を変更する方法【iOS 10】

UITabBarControllerで、タブバーアイコン非選択時の

→もっと見る

PAGE TOP ↑