iOS 16で登場したLayout protocolというのを使うと柔軟なレイアウトができるようなので調べてみました
使い方ですが、まずは下のようにCustomLayoutを定義します
CustomLayoutはVStackやHStackのように親ビューとして使う事ができます
struct CustomLayout: Layout {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
return CGSize(width: 100, height: 100)
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
subviews.enumerated().forEach {
$0.element.place(
at: CGPoint(x: Int(bounds.origin.x) + $0.offset * 10,
y: Int(bounds.origin.y) + $0.offset * 10),
anchor: .topLeading,
proposal: .unspecified)
}
}
}
実際に使っているコードは下のものです
CustomLayoutの下にTextを2つ並べています
struct ContentView: View {
var body: some View {
CustomLayout {
Text("1")
Text("2")
}.background(.red)
}
}
起動すると下のような表示になります
続いてLayout protocolで使っている2つのメソッドについて見ていきます
sizeThatFitsですがこれはCustomLayoutのサイズを決めるものです
今回は100x100のサイズ固定にしました
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
return CGSize(width: 100, height: 100)
}
通常はsubviewのサイズを元に調整する事が多いかと思います
小ビューの大きさはsizeThatFitsで取れるのでそれを利用します
subviews.forEach { print($0.sizeThatFits(.unspecified)) }
もう一つのplaceSubviewsはサブビューの位置を決めるものです
placeメソッドで位置を決めています
今回は1つ目の要素が0x0の位置、2つ目は10x10の位置というように斜めに配置しました
位置ですがbounds.origin.xとyを足さないと画面の左上に配置されてしまうので注意が必要です
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
subviews.enumerated().forEach {
$0.element.place(
at: CGPoint(x: Int(bounds.origin.x) + $0.offset * 10,
y: Int(bounds.origin.y) + $0.offset * 10),
anchor: .topLeading,
proposal: .unspecified)
}
}
CustomLayoutですがframeで大きさを決める事も可能です
ただframeはplaceSubviewsが走った後に反映されるので使わない方が良さそうです
CustomLayout {
Text("1")
Text("22")
}.frame(width: 200, height: 200).background(.red)
参考URL
SwiftUIのLayout protocolについて #iOS - Qiita