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コード)

関連記事

20140721-170305.jpg

プロトタイピングのツールや手法を学べる「プロトタイピング実践ガイド」

株式会社インプレスさまより献本御礼。 プロトタイピング実践ガイド スマホアプリの効率的なデザイ

記事を読む

I20160119-155700.jpg

Xcodeで80文字目のところにガイドラインを表示する方法

Xcodeで80文字目のところに線(ガイドライン)を表示する方法について解説します! 「ソース

記事を読む

20141224-231137.jpg

【Swift】Auto Layoutで特定のデバイス・画面サイズの時だけ制約を変更する方法

Auto Layoutで、特定のデバイスや画面サイズの時だけ制約を変更する方法について紹介したい

記事を読む

20150410-122409.jpg

複数バージョンのXcodeを共存してMacにインストールする方法

いよいよXcode 6.3が正式にリリースされましたね。 今回は複数バージョンのXco

記事を読む

I20160102-131507.jpg

Swift 2の文法が分かるオススメ本「詳解 Swift 改訂版」

Swift文法書の定番とも言える、荻原 剛志さんの「詳解 Swift」。 Swiftの文法をキ

記事を読む

20140916-130550

Swiftの列挙型、switch文、網羅性チェックが素晴らしい!

アップルの新プログラミング言語「Swift」をちょっとずついじってるんですが、列挙型とswitch文

記事を読む

20141027-113224.jpg

【Swift】「詳解 Objective-C 2.0 第3版」の著者による「詳解Swift」が予約受付中!

「詳解 Objective-C 2.0 第3版」の筆者「荻原 剛志」さんによる新しい技術書「詳細

記事を読む

I20160918-133158.jpg

StoryboardでChild View Controllerにデータを渡す方法(Xcode 8 & Swift 3)

Storyboardで、Child View Controllerにデータを渡す方法を紹介します

記事を読む

I20160122-095132.jpg

Swift 2のドキュメントコメントの書き方(JavaDoc的なやつ)

コード補完時やoptionクリック時などに説明が出てくるようにする「ドキュメントコメント(J

記事を読む

20150406-121104.jpg

【アプリ開発】選んだ色をUIColorの生成コードに変換できる「Developer Color Picker」

選んだ色をUIColorの生成コードに変換できる「Developer Color Picker」

記事を読む

I20170521-225453.jpg
東京駅八重洲口の「羊肉酒場 悟大」で網焼きジンギスカンを頂きました!

ゆうせいさんと株式会社 大庄さんからご招待頂き、悟大withサッポ

I20170514-165235.jpg
iPhoneと連携できる体重体組成計「Withings Body Cardio」を使ってます

ジムに通い始めて体脂肪率が落ち始めたのをキッカケに、iPhoneと

I20170507-155440.jpg
【派手髪】ハーレイクイン風の髪色に染めてもらいました

2016年10月20日、ハーレイクイン風の髪色に染めてもらいました

I20170504-173110.jpg
【メンズネイル】東京・新宿のネイルサロンでターコイズのホログラムネイルしてもらった

ネイルネタが1年分くらい溜まっているので、ちょっとずつ書いていこう

I20170502-010117.jpg
SNUGGのライトニングケーブルが耐久性高し。8ヶ月使ってますが断線の気配なし!

iPhoneの充電&転送ケーブルであるLightningケーブルっ

→もっと見る

PAGE TOP ↑