SwiftUIでスクロールビューの挙動を決める defaultScrollAnchor の第二引数 ScrollAnchorRole
はじめに
iOS18のSwiftUIに defaultScrollAnchor(_ anchor: UnitPoint?, for role: ScrollAnchorRole)
という面白い引数を持つものが登場したので紹介します。
これまでのあらすじ
iOS17にて導入された defaultScrollAnchor(_ anchor: UnitPoint?)
はスクロールビューの表示範囲よりも内容の方が小さいときに表示範囲のどこに表示するか設定できる画期的なものでした。
import SwiftUI
struct OldStyle: View {
let items: [String] = [
"aaa", "bbb", "ccc", "ddd", "eee"]
var body: some View {
Spacer()
ScrollView {
ForEach(items, id: \.self) { item in
Text(item)
.font(.title)
}
}
.defaultScrollAnchor(.bottom) //ここです
.frame(width: 200, height:300)
.padding()
.border(.black)
Spacer()
}
}
#Preview {
OldStyle()
}
iOS18で追加された for という引数
従来の仕様ではちょっとした問題があります。
野球の試合の速報をイメージしましょう。3回の裏が終わった時点で表示は左のようになります(試合の内容は省く)。上から表示する自然な表示です。
9回の裏の攻撃中に画面を開くと右のようになりました。
ユーザーは現在の内容を見たいのですが、表示されていません(もちろんスクロールすれば表示します)。
そこでこの新しいメソッドが出てきます。
defaultScrollAnchor(_ anchor: UnitPoint?, for role: ScrollAnchorRole)
です。
この2番目の引数 ScrollAnchorRole
はRoleという名の通り、1番目の引数がどういう役割か指定します。
役割は3種類あります。initialOffset
sizeChanges
alignment
です。それぞれを説明します。
initialOffset
今度は表示内容>表示範囲のときにどこで揃えるかというものです。先程の例でいうと .top
なら1回表の位置で揃え、.bottom
なら9回裏の位置で揃えます。
sizeChanges
これはスクロールビューの大きさを変えたときに、表示内容のどこを固定するかというもののようです。表示内容>表示範囲のときに関係するという点で initialOffset
と似ているのですが、initialOffset
は表示した瞬間どうするか、sizeChanges
はその後のサイズ変更に対する挙動というものです。
alignment
これは、表示内容<表示範囲のときにどの場所に表示するかというものです。 1番目の引数が .top
なら上に表示、.bottom
なら下に表示という意味になります。
試合はまだ終わっていない
さっそくこれを適用して9回裏を表示させましょう。
出来ました。
struct NewStyle: View {
let items: [String] = [
"1回表", "1回裏", "2回表", "2回裏", "3回表", "3回裏",
"4回表", "4回裏", "5回表", "5回裏", "6回表", "6回裏",
"7回表", "7回裏", "8回表", "8回裏", "9回表", "9回裏",
]
var body: some View {
Spacer()
ScrollView {
ForEach(items, id: \.self) { item in
Text(item)
.font(.title)
}
}
.defaultScrollAnchor(.top, for: .alignment)
.defaultScrollAnchor(.bottom, for: .initialOffset)
.frame(width: 200, height:300)
.padding()
.border(.black)
Spacer()
}
}
Appleによる別の書き方
iOS17からあった
.defaultScrollAnchor(.bottom)
という書き方は上の3つの役割をすべて設定するものです。よって
.defaultScrollAnchor(.bottom)
.defaultScrollAnchor(.topLeading, for: .alignment)
という書き方も出来るようです。
sizeChangesの記事を追加しました
挙動が複雑なのでこれだけで独立してます
Discussion