ちょっと話題の記事

[iOS 11][Xcode 9][新機能] UITableViewのSelf-Sizingがデフォルトになりました

2017.09.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

UITableViewのSelf-Sizingがデフォルトになった

Xcode 9(iOS 11 SDK)でUITableViewのSelf-Sizing(Auto Layoutによる高さの自動調整)がデフォルトになりました。

これまではセルの高さをSelf-Sizingにしたい場合は以下のようなコードを書く必要がありました。
参考:自律的に寸法を調整するテーブルビューのセルの活用

tableView.estimatedRowHeight = 85.0
tableView.rowHeight = UITableViewAutomaticDimension

Xcode 9ではSelf-Sizingがデフォルトになったので上記のコードを書く必要がなくなりました。

検証環境

本記事は以下の環境で検証を行っています。

  • macOS Sierra バージョン 10.12.6
  • Xcode Version 9.0 (9A235)
  • Swift 4.0
  • iPhone X シミュレーター iOS 11.0

やってみた

実行結果

先に実行結果を載せておきます。文字数に応じてセルの高さが変わっていることが確認できます。

xcode_9_self_sizing

UITableViewCellにUILabelを配置し、Auto Layoutの制約をつける

まずはstoryboardでUITableViewをDynamic Prototypesで配置し、UITableViewCellを1つ用意します。(今回はMyCustomCellというサブクラスにしました)
用意したセルにUILabelを1つ配置し、上下左右にAuto Layoutの制約をつけます。

xcode_9_self_sizing_label_constraints

そしてUILabelが複数行表示できるように「Lines」を0にしておきます。

xcode_9_self_sizing_label_lines

UILabelの内容を設定するコードを書く

レイアウトが出来たらUILabelのテキストを設定するコードを書きます。 ちょっと長いですがテキストの内容が長いだけです。
ご覧の通り、Self-Sizingを設定するコードは一切ありません( ´◡` )

//
//  ViewController.swift
//  Xcode9SelfSizingCells
//
//  Created by kato.jun on 2017/09/25.
//  Copyright © 2017年 Classmethod, Inc. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

    private let labelTextArray = ["わがはいは猫である。名前はまだ無い。",
                                  "どこで生れたかとんと見当けんとうがつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。",
                                  """
吾輩はここで始めて人間というものを見た。\
しかもあとで聞くとそれは書生という人間中で一番獰悪どうあくな種族であったそうだ。\
この書生というのは時々我々を捕つかまえて煮にて食うという話である。\
しかしその当時は何という考もなかったから別段恐しいとも思わなかった。\
ただ彼の掌てのひらに載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。
""",
"""
掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始みはじめであろう。\
この時妙なものだと思った感じが今でも残っている。\
第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶やかんだ。\
その後ご猫にもだいぶ逢あったがこんな片輪かたわには一度も出会でくわした事がない。\
のみならず顔の真中があまりに突起している。\
そうしてその穴の中から時々ぷうぷうと煙けむりを吹く。\
どうも咽むせぽくて実に弱った。これが人間の飲む煙草たばこというものである事はようやくこの頃知った。\
この書生の掌の裏うちでしばらくはよい心持に坐っておったが、しばらくすると非常な速力で運転し始めた。\
書生が動くのか自分だけが動くのか分らないが無暗むやみに眼が廻る。胸が悪くなる。\
到底とうてい助からないと思っていると、どさりと音がして眼から火が出た。\
それまでは記憶しているがあとは何の事やらいくら考え出そうとしても分らない。
ふと気が付いて見ると書生はいない。たくさんおった兄弟が一疋ぴきも見えぬ。肝心かんじんの母親さえ姿を隠してしまった。\
その上今いままでの所とは違って無暗むやみに明るい。眼を明いていられぬくらいだ。\
はてな何でも容子ようすがおかしいと、のそのそ這はい出して見ると非常に痛い。吾輩は藁わらの上から急に笹原の中へ棄てられたのである。
""",
"""
ようやくの思いで笹原を這い出すと向うに大きな池がある。吾輩は池の前に坐ってどうしたらよかろうと考えて見た。\
別にこれという分別ふんべつも出ない。しばらくして泣いたら書生がまた迎に来てくれるかと考え付いた。\
ニャー、ニャーと試みにやって見たが誰も来ない。そのうち池の上をさらさらと風が渡って日が暮れかかる。\
腹が非常に減って来た。泣きたくても声が出ない。\
仕方がない、何でもよいから食物くいもののある所まであるこうと決心をしてそろりそろりと池を左ひだりに廻り始めた。\
どうも非常に苦しい。そこを我慢して無理やりに這はって行くとようやくの事で何となく人間臭い所へ出た。\
ここへ這入はいったら、どうにかなると思って竹垣の崩くずれた穴から、とある邸内にもぐり込んだ。\
縁は不思議なもので、もしこの竹垣が破れていなかったなら、吾輩はついに路傍ろぼうに餓死がししたかも知れんのである。\
一樹の蔭とはよく云いったものだ。この垣根の穴は今日こんにちに至るまで吾輩が隣家となりの三毛を訪問する時の通路になっている。\
さて邸やしきへは忍び込んだもののこれから先どうして善いいか分らない。\
そのうちに暗くなる、腹は減る、寒さは寒し、雨が降って来るという始末でもう一刻の猶予ゆうよが出来なくなった。\
仕方がないからとにかく明るくて暖かそうな方へ方へとあるいて行く。今から考えるとその時はすでに家の内に這入っておったのだ。\
ここで吾輩は彼かの書生以外の人間を再び見るべき機会に遭遇そうぐうしたのである。\
第一に逢ったのがおさんである。これは前の書生より一層乱暴な方で吾輩を見るや否やいきなり頸筋くびすじをつかんで表へ抛ほうり出した。\
いやこれは駄目だと思ったから眼をねぶって運を天に任せていた。\
しかしひもじいのと寒いのにはどうしても我慢が出来ん。吾輩は再びおさんの隙すきを見て台所へ這はい上あがった。\
すると間もなくまた投げ出された。吾輩は投げ出されては這い上り、這い上っては投げ出され、何でも同じ事を四五遍繰り返したのを記憶している。\
その時におさんと云う者はつくづくいやになった。この間おさんの三馬さんまを偸ぬすんでこの返報をしてやってから、やっと胸の痞つかえが下りた。\
吾輩が最後につまみ出されようとしたときに、この家うちの主人が騒々しい何だといいながら出て来た。\
下女は吾輩をぶら下げて主人の方へ向けてこの宿やどなしの小猫がいくら出しても出しても御台所おだいどころへ上あがって来て困りますという。\
主人は鼻の下の黒い毛を撚ひねりながら吾輩の顔をしばらく眺ながめておったが、やがてそんなら内へ置いてやれといったまま奥へ這入はいってしまった。\
主人はあまり口を聞かぬ人と見えた。下女は口惜くやしそうに吾輩を台所へ抛ほうり出した。\
かくして吾輩はついにこの家うちを自分の住家すみかと極きめる事にしたのである。\
吾輩の主人は滅多めったに吾輩と顔を合せる事がない。職業は教師だそうだ。\
学校から帰ると終日書斎に這入ったぎりほとんど出て来る事がない。家のものは大変な勉強家だと思っている。\
当人も勉強家であるかのごとく見せている。しかし実際はうちのものがいうような勤勉家ではない。\
吾輩は時々忍び足に彼の書斎を覗のぞいて見るが、彼はよく昼寝ひるねをしている事がある。時々読みかけてある本の上に涎よだれをたらしている。\
彼は胃弱で皮膚の色が淡黄色たんこうしょくを帯びて弾力のない不活溌ふかっぱつな徴候をあらわしている。\
その癖に大飯を食う。大飯を食った後あとでタカジヤスターゼを飲む。飲んだ後で書物をひろげる。\
二三ページ読むと眠くなる。涎を本の上へ垂らす。これが彼の毎夜繰り返す日課である。吾輩は猫ながら時々考える事がある。\
教師というものは実に楽らくなものだ。人間と生れたら教師となるに限る。こんなに寝ていて勤まるものなら猫にでも出来ぬ事はないと。\
それでも主人に云わせると教師ほどつらいものはないそうで彼は友達が来る度たびに何とかかんとか不平を鳴らしている。
吾輩がこの家へ住み込んだ当時は、主人以外のものにははなはだ不人望であった。どこへ行っても跳はね付けられて相手にしてくれ手がなかった。\
いかに珍重されなかったかは、今日こんにちに至るまで名前さえつけてくれないのでも分る。\
吾輩は仕方がないから、出来得る限り吾輩を入れてくれた主人の傍そばにいる事をつとめた。朝主人が新聞を読むときは必ず彼の膝ひざの上に乗る。\
彼が昼寝をするときは必ずその背中せなかに乗る。これはあながち主人が好きという訳ではないが別に構い手がなかったからやむを得んのである。\
その後いろいろ経験の上、朝は飯櫃めしびつの上、夜は炬燵こたつの上、天気のよい昼は椽側えんがわへ寝る事とした。\
しかし一番心持の好いのは夜よに入いってここのうちの小供の寝床へもぐり込んでいっしょにねる事である。\
この小供というのは五つと三つで夜になると二人が一つ床へ入はいって一間ひとまへ寝る。\
吾輩はいつでも彼等の中間に己おのれを容いるべき余地を見出みいだしてどうにか、こうにか割り込むのであるが、運悪く小供の一人が眼を醒さますが最後大変な事になる。\
小供は――ことに小さい方が質たちがわるい――猫が来た猫が来たといって夜中でも何でも大きな声で泣き出すのである。\
すると例の神経胃弱性の主人は必かならず眼をさまして次の部屋から飛び出してくる。\
現にせんだってなどは物指ものさしで尻ぺたをひどく叩たたかれた。
吾輩は人間と同居して彼等を観察すればするほど、彼等は我儘わがままなものだと断言せざるを得ないようになった。\
ことに吾輩が時々同衾どうきんする小供のごときに至っては言語同断ごんごどうだんである。\
自分の勝手な時は人を逆さにしたり、頭へ袋をかぶせたり、抛ほうり出したり、へっついの中へ押し込んだりする。\
しかも吾輩の方で少しでも手出しをしようものなら家内かない総がかりで追い廻して迫害を加える。\
この間もちょっと畳で爪を磨といだら細君が非常に怒おこってそれから容易に座敷へ入いれない。\
台所の板の間で他ひとが顫ふるえていても一向いっこう平気なものである。\
吾輩の尊敬する筋向すじむこうの白君などは逢あう度毎たびごとに人間ほど不人情なものはないと言っておらるる。\
白君は先日玉のような子猫を四疋産うまれたのである。ところがそこの家うちの書生が三日目にそいつを裏の池へ持って行って四疋ながら棄てて来たそうだ。\
白君は涙を流してその一部始終を話した上、どうしても我等猫族ねこぞくが親子の愛を完まったくして美しい家族的生活をするには人間と戦ってこれを剿滅そうめつせねばならぬといわれた。\
一々もっともの議論と思う。また隣りの三毛みけ君などは人間が所有権という事を解していないといって大おおいに憤慨している。\
元来我々同族間では目刺めざしの頭でも鰡ぼらの臍へそでも一番先に見付けたものがこれを食う権利があるものとなっている。\
もし相手がこの規約を守らなければ腕力に訴えて善よいくらいのものだ。\
しかるに彼等人間は毫ごうもこの観念がないと見えて我等が見付けた御馳走は必ず彼等のために掠奪りゃくだつせらるるのである。\
彼等はその強力を頼んで正当に吾人が食い得べきものを奪うばってすましている。白君は軍人の家におり三毛君は代言の主人を持っている。\
吾輩は教師の家に住んでいるだけ、こんな事に関すると両君よりもむしろ楽天である。\
ただその日その日がどうにかこうにか送られればよい。いくら人間だって、そういつまでも栄える事もあるまい。まあ気を永く猫の時節を待つがよかろう。
"""]

    override func viewDidLoad() {
        super.viewDidLoad()

        setup()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        tableView.flashScrollIndicators()
    }

    private func setup() {
        tableView.dataSource = self
        tableView.delegate = self
    }
}

// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return labelTextArray.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCustomCell", for: indexPath) as! MyCustomCell
        cell.label.text = labelTextArray[indexPath.row]
        return cell
    }
}

// MARK: - UITableViewDelegate
extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    }
}
import UIKit

class MyCustomCell: UITableViewCell {
    @IBOutlet weak var label: UILabel!
}

Self-Sizingの設定はSize inspectorにあります

コードではSelf-Sizingの設定はしていませんが、Self-Sizingの設定はUITableViewのSize inspectorにあります。

xcode_9_self_sizing_settings

Row HeightとEstimateがデフォルトでAutomaticになっています。
また、デフォルトではありませんがセクションのHeaderやFooterもAutomaticにできます。

Self-Sizingを無効にするには

Self-Sizingを無効にするには以下のように見積もり高さに0を設定します。

// セルの高さ
tableView.estimatedRowHeight = 0

// セクションヘッダー
tableView.estimatedSectionHeaderHeight = 0

// セクションフッター
tableView.estimatedSectionFooterHeight = 0

おわりに

UITableViewのSelf-Sizingはよく使うのでデフォルトの挙動になって良かったと思います。

参考