SwiftUI でリスト表示
驚くほどの少ないソースコードでUI画面を表示できるSwiftUIは、リスト(テーブル)表示も従来とはまったく変わります。
※この記事ではSwift言語(最新の5.1)の基本的な知識を前提にしています。コード内のキーワードや書式などの不明点は Swift5初級ガイドなどを参照してください。
※SwiftUIについて全体的なことは『SwiftUI最初の一歩』、コードの基本は『SwiftUIの文法 その1 View』を参照してください
毎月札幌でiOSアプリ作りをアシストするセミナーをやっています。1時間にわたるセミナーの全内容を、物理的に参加できない方のためにnote上で公開します。
お知らせ
電子書籍『Swift5初級ガイド』をAppleのブックストアから出しました。サンプルは無料です。MacでもiPadでもiPhoneでも読めます。
WWDC2020で発表されたSwift 5.3に対応した第6版がダウンロード可能です。
(ご購入済みの場合は無料アップデートです)
ブックストアから一度購入すると今後のアップデートは無料で読めます。
iOSアプリ作りをアシストするセミナーは今後も月一回のペースで続ける予定です。(2020年3月以降COVID-19感染拡大防止のため休止しています)
詳細は connpass.com の 札幌Swift でご確認ください。そして機会があればぜひ参加してください。
アプリ作りやプログラミング教育に関連する話題は 札幌Swift のfacebookページ で発信しています。
・画像クリックで拡大表示できます
・画像を拡大表示中は画像の左右をクリックで画像だけを順に表示できます
・ソースコード部分は左右にスクロールできます
Xcode は 11.3 を使っていています。
1 SwiftUIでのリストとその仲間
SwiftUIのリスト(テーブル)表示もビューです。
List型が代表です。UIKit の UITableView に相当します。
今回は表示にのみ注目します。削除や移動などの編集は説明しません。
Form型もあります。
こちらは設定アプリのようなコントロール類のレイアウト用につくられたものです。
VStack型はビューを縦にレイアウトします。
これらの型でレイアウトする各行はすべてSwiftUIのビューをそのまま利用します。
Cocoa touch あるいは Cocoa のようなセルは使いません。
セル(Cell)はもともとテーブル表示のスクロール動作などの負荷を下げることが目的の、処理を簡略化したビューの一種でした。
SwiftUIのビューの表示は軽く、スクロールはなめらかな動きを保つことができます。
SwiftUIではCellを再利用するコードも不要です。
ほかにもいくつかの型があります。順に説明します。
2 List
List型は複数のビューを縦に並べて表示します。
全体は上下にスクロールできます。リスト内の各ビュー(行)は区切り線を表示します。
Playground用のサンプルコードです。
import SwiftUI
import PlaygroundSupport
struct Sample01View: View {
var body: some View {
List {
// 一行目
Text("001")
// 二行目
Text("002")
// 三行目
Text("Listは上下にスクロール可能")
}
}
}
// playgroundで実行する場合に必要なコード
PlaygroundPage.current.liveView = UIHostingController(rootView: Sample01View())
1行目と2行目の import 文と最後の文はすべてのサンプルで必要です。
// サンプルの冒頭に必要な文
import SwiftUI
import PlaygroundSupport
// サンプルコードの最後に必要な文
// playgroundで実行する場合に必要なコード
PlaygroundPage.current.liveView = UIHostingController(rootView: Sample01View())
Xcode 11.3での実行例
2-1 固定表示
上記はList型の最もシンプルなイニシャライザを利用した例です。
// List型イニシャライザ
init(@ViewBuilder content: () -> Content)
このイニシャライザの引数はクロージャ内に表示するビューを順に書きます。
引数がクロージャで一つだけのためにトレイリングクロージャで書く場合が多く、イニシャライザの()も省略し List { と書くことができます。
@ViewBuilder は複数のビューを扱うためのカスタムパラメータ属性です。
@ViewBuilder ではクロージャ内に最大10個のビューまであつかうことができます。
なお11行以上のビューを表示するにはいくつかの方法があり、順に説明します。
2-2 行高さは自動計算
各行は区切り線が表示されます。
行の高さはビューの高さになります。
VStackはビューを縦にレイアウトするためのビューです。
(最初のimport文2行と最後のライブビュー指定行は割愛しています)
// 行高さが不揃いの例【bodyプロパティのみ】
var body: some View {
List {
// 一行目
VStack(alignment:.leading){
Text("001")
.font(.largeTitle)
Text("説明文 このように1行の高さは自動計算")
.font(.title)
}
// 二行目
Text("002")
.font(.title)
// 三行目
VStack(alignment:.leading){
Text("003")
.font(.title)
Text("札幌Swift")
.font(.largeTitle)
}
}
}
2-3 Range型で行数を指定
次に範囲(Range型)で行数を指定するイニシャライザを紹介します。
引数は二つです。
一つ目がInt型の範囲です。このサンプルでは 1..<3 です。
二つ目はレイアウトするビューです。(トレイリングクロージャでカッコの直後に出して記述できます)
このサンプルでは1から2までの範囲になります。
このイニシャライザは範囲内を順に繰り返し二つ目の引数で各行共通の表示用ビューを指定します。
// 行インデックスの範囲を指定する方法
struct Sample01View: View {
var body: some View {
List(1..<3) { localIndex in
// すべての行で共通のビュー
Text("\(localIndex)")
}
}
}
このサンプルでは範囲を 1..<3 と指定しています。
この範囲で行インデックスを繰り返し、クロージャの localIndex でインデックスを受け取り、すべての行で共通のビュー Text("\(localIndex)") で表示するコードです。
このコードは Xcode 11.3 のプレイグラウンドで実行すると、ライブビューを表示するまで時間がかかりました。
【2020年8月1日追記:Xcode 11.6では問題ないことを確認しました】
調べてみると次の画面のようにデバグエリアにたくさんのログを出力しています。
Xcode のプレイグラウンドだけの問題で、アプリにするとシミュレータで1000行でも問題なく、実機に転送して実行では10000行でも問題なく実行できました。
【Xcode 11.6のプレイグラウンドは10000行で問題ないことを確認しました】
iPad の Playgrounds 3.2 でも特に問題なく表示できます。
Xcode 11.3 のiOS用プレイグラウンドだけの問題のようです。
SwiftUI でテーブルを表示する場合データソースなどは不要です。
UIKit ではデータソースでテーブルに表示する内容を決めていました。
SwiftUIはとてもシンプルです。body プロパティにすべて書きます。
2-4 Range型で短縮型引数名$0を利用
List(1..<3) { の書き方で、クロージャ内で短縮型引数名$0を使うこともできます。
$0(先頭の引数を意味します) を使うとクロージャの引数名と in キーワードは書きません。
// 行インデックスの範囲を指定する方法
struct Sample01View: View {
var body: some View {
List(1..<3) {
// すべての行で共通のビュー
Text("共通表示ビュー 行番号:\($0)")
}
}
}
2-5 配列内容を表示
配列を指定して表示することもできます。
残念ながら私の環境(macOS10.14.6のiMac、Xcode 11.3のプレイグラウンド)では、次のサンプル(13行を表示)はたくさんのログを出力し5分たってもライブビューを表示できません。
【macOS10.15.6のXcode 11.6では問題なく表示できました】
iPadのPlaygrounds 3.2 では問題なく表示します。
Xcodeのプレイグラウンドでも各行のQuick Lookでは表示を確認できます。
(▲注意:Xcodeのプレイグラウンドで実行する場合はdataの件数をまず2件で試してください)
// 配列を表示するリスト
import SwiftUI
import PlaygroundSupport
// リストに表示するデータのための型
struct Author: Identifiable {
let id: Int
let name: String
}
struct Sample01View: View {
// データはイニシャライザで渡す
var Authors: [Author]
var body: some View {
List(Authors) { author in
// すべての行で共通のビュー
Text("\(author.name)")
.font(.title)
.padding()
}
}
}
// リストに表示するデータ idはユニークであれば順番は関係しない
let data: [Author] = [
Author(id: 0, name: "芥川龍之介"),
Author(id: 2, name: "石川啄木")
// Xcode の場合は以下をコメントにしてください
, Author(id: 3, name: "江戸川乱歩"),
Author(id: 4, name: "太宰治"),
Author(id: 5, name: "谷崎潤一郎"),
Author(id: 6, name: "中谷宇吉郎"),
Author(id: 7, name: "夏目漱石"),
Author(id: 8, name: "宮沢賢治"),
Author(id: 9, name: "野村胡堂"),
Author(id: 10, name: "森 鴎外"),
Author(id: 11, name: "山本周五郎"),
Author(id: 12, name: "吉川英治")
// Xcodeの場合はここまでコメントアウト
]
// playgroundで実行する場合に必要なコード
PlaygroundPage.current.liveView = UIHostingController(rootView: Sample01View(Authors:data))
このサンプルでは、まず Identifiable プロトコルに準拠する型 Author を配列のインスタンス用の型とします。
この準備をしておくと引数二つの List 型イニシャライザが利用できます。
一つ目の引数が配列、二つ目は配列の要素を引数としたクロージャです。
今後も記事を増やすつもりです。 サポートしていただけると大変はげみになります。