🔨
SwiftUIのスクロールビューで、要素数が多い時は設定したサイズに収め、少ない時はセンタリングするのをLayoutを使って実現
はじめに
SwiftUIでスクロールビューを使う時、要素数が多い時は設定したサイズ以内に収め、少ない時はセンタリングする(表示に必要な高さに縮まる)やり方がわからなかったがLayoutを使ったら限定的な用途で出来た。(他に何かいいやり方があるかもしれないが)
環境
Xcode 15.4
スクリーンショット
注意
まだ研究中なので使用する時は注意してください。
Text()に与える文字列の長さが長くて2行になるとズレます。
これは自然な大きさを問い合わせたときは1行で表示した大きさを答えるが、表示するときには2行になるからです。
コード
import SwiftUI
struct ContentView: View {
var body: some View {
VStack() {
Spacer()
Text("上の要素")
Centered {
ScrollView {
ForEach(1..<5) {_ in
Text("abcdefg")
}
}
.scrollBounceBehavior(.basedOnSize)
.frame(maxHeight: 300.0)
}
.border(.gray)
.frame(height: 300.0)
Text("下の要素")
Spacer()
}
.padding()
}
}
struct Centered: Layout {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
guard subviews.count == 1 else {
fatalError()
}
let subviewSize = subviews[0].sizeThatFits(.unspecified)
print("proposal",proposal)
print("subviewSize",subviewSize)
print(min(proposal.width ?? CGFloat.greatestFiniteMagnitude, subviewSize.width))
print(min(proposal.height ?? CGFloat.greatestFiniteMagnitude, subviewSize.height))
return CGSize(
width: min(proposal.width ?? CGFloat.greatestFiniteMagnitude, subviewSize.width),
height: min(proposal.height ?? CGFloat.greatestFiniteMagnitude, subviewSize.height))
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
guard subviews.count == 1 else {
fatalError()
}
let subviewSize = subviews[0].sizeThatFits(.unspecified)
let x = bounds.minX
let y = bounds.minY
print("bounds", bounds)
print("proposal", proposal)
print("subviewSize", subviewSize)
subviews[0]
.place(
at: CGPoint(x: x, y: y),
anchor: .topLeading,
proposal: ProposedViewSize(bounds.size)
)
}
}
Discussion