ScrollAnchorRole.sizeChanges 調査隊
環境
Sequoia 15.0
Xcode 16.0
ScrollAnchorRole.sizeChangesとは
スクロールビューの大きさやその表示内容が変化したときに、変化前/変化後の違和感を軽減するもの。
一番使われるのは、表示内容の一番最後の要素に特に意味があるとき、サイズが変化しても一番最後のものを表示し続ける使い方かと。
前提知識
そもそもScrollAnchorRoleで表されるの3つの役割を知らないといけない。
次の記事の内容が前提になります。
先にまとめ
あまりにややこしいので先にまとめる。
大きいルールは3つ
基本はトップ
.sizeChanges = .topのときはもちろん、他のときでも条件が合わなければトップの位置を基準にする
.sizeChanges = .centerのとき
現在の表示位置がセンターであると判断されれば、サイズの変化時にセンターを基準にする
.sizeChanges = .bottomのとき
一番最後の要素が表示されていれば、一番最後の要素を表示し続ける挙動をする
.sizeChanges = .top
トップ位置を基準にする。
initialOffset | alignment | 結果 |
---|---|---|
top | top | OK |
top | center | OK |
top | bottom | OK |
center | top | OK |
center | center | OK |
center | bottom | OK |
bottom | top | OK |
bottom | center | OK |
bottom | bottom | OK |
OKというのは、予想通りの挙動ですわ、という意味。
.sizeChanges = .center
以下のルールAからCが適用されたときはセンター基準の挙動
それ以外はトップ基準の挙動
ルールA
内容<表示領域となりalignment = .topが適用されてから、内容>表示領域になったのち、手動でスクロールしない
ルールB
内容<表示領域となりalignment = .centerが適用されてから、内容>表示領域になったのち、手動でスクロールしない
ルールC
はじめに表示したときに内容>表示領域で、initialOffset = .centerが適用されたのち、手動でスクロールしない
◯はそのルールの適用がありえるという意味。
initialOffset | alignment | ルールA | ルールB | ルールC | |
---|---|---|---|---|---|
top | top | ◯ | |||
top | center | ◯ | |||
top | bottom | 常にトップ基準の挙動 | |||
center | top | ◯ | ◯ | ||
center | center | ◯ | ◯ | ||
center | bottom | ◯ | |||
bottom | top | ◯ | |||
bottom | center | ◯ | |||
bottom | bottom | 常にトップ基準の挙動 |
.alignment = .bottomのときにルールAやBのようなものが適用されないが、問題になることもあまりなさそう。
.sizeChanges = .centerにする人はすべて.centerにして使うことが多いのかなという気がする。
スクロールしてしまうと「現在センターである」という状態が失われる(と推測)。
.sizeChanges = .bottom
以下のルールDからFが適用されたときボトム基準の挙動
それ以外はトップ基準の挙動
ルールD
内容>表示領域であり、一番下の要素が表示されているとき
ルールE
内容<表示領域となりalignment = .topが適用されたのち、内容>表示領域に変わったとき(その後はルールDに以降)
ルールF
内容<表示領域となりalignment = .bottomが適用されたのち、内容>表示領域に変わったとき(その後はルールDに以降)
initialOffset | alignment | ルールD | ルールE | ルールF | |
---|---|---|---|---|---|
top | top | ◯ | ◯ | ||
top | center | ◯ | * | ||
top | bottom | ◯ | ◯ | ||
center | top | ◯ | ◯ | ||
center | center | ◯ | * | ||
center | bottom | ◯ | ◯ | ||
bottom | top | ◯ | ◯ | ||
bottom | center | ◯ | * | ||
bottom | bottom | ◯ | ◯ |
- やり方を変えたらルールEやFに似たものが適用されるかも
調査コード
import SwiftUI
struct ContentView: View {
let items: [String] = [
"aaa", "bbb", "ccc", "ddd", "eee", "fff",
"ggg", "hhh", "iii", "jjj", "kkk", "lll",
"mmm", "nnn", "ooo", "ppp", "qqq", "rrr",
"sss", "ttt", "uuu", "vvv", "www", "xxx",
"yyy", "zzz"]
//これらのパラメータははじめに表示されたときの内容と領域の大小をコントロールするものです
@State var size1 = 0.0
@State var size2 = 100.0
let initialOffsetUP: UnitPoint = .bottom
let sizeChangesUP: UnitPoint = .bottom
let alignmentUP: UnitPoint = .bottom
var body: some View {
HStack {
VStack {
Button("larger") {
size2 -= 20
if size2 < 0 {
size2 = 0
}
}
.padding()
Button("smaller") {
size2 += 20
}
.padding()
Spacer()
.frame(height: size2)
ScrollView {
ForEach(items, id: \.self) { item in
Text(item)
}
}
.defaultScrollAnchor(initialOffsetUP, for: .initialOffset)
.defaultScrollAnchor(sizeChangesUP, for: .sizeChanges)
.defaultScrollAnchor(alignmentUP, for: .alignment)
.padding()
.border(.black)
Spacer()
.frame(height: size2)
}
VStack {
Button("larger") {
size1 -= 20
if size1 < 0 {
size1 = 0
}
}
.padding()
Button("smaller") {
size1 += 20
}
.padding()
Spacer()
.frame(height: size1)
ScrollView {
ForEach(items, id: \.self) { item in
Text(item)
}
}
.defaultScrollAnchor(initialOffsetUP, for: .initialOffset)
.defaultScrollAnchor(sizeChangesUP, for: .sizeChanges)
.defaultScrollAnchor(alignmentUP, for: .alignment)
.padding()
.border(.black)
Spacer()
.frame(height: size1)
}
}
}
}
#Preview {
ContentView()
}
Discussion