Created
August 31, 2022 08:20
-
-
Save swiftui-lab/a695ceb5977e32346df095fd16cf3f55 to your computer and use it in GitHub Desktop.
An example of a composed layout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Author: SwiftUI-Lab (swiftui-lab.com) | |
// Description: A demonstration of a composed Layout | |
// blog article: https://swiftui-lab.com/layout-protocol-part-2 | |
import SwiftUI | |
struct ContentView: View { | |
let colors: [Color] = [.yellow, .orange, .red, .pink, .purple, .blue, .cyan, .green] | |
@State var viewCount: Double = 9 | |
var body: some View { | |
let indices = Array<Int>(repeating: 0, count: Int(viewCount)).indices | |
VStack { | |
Spacer() | |
ComposedLayout { | |
ForEach(indices, id: \.self) { idx in | |
RoundedRectangle(cornerRadius: 8) | |
.fill(colors[idx%colors.count].opacity(0.8)) | |
.frame(height: 50) | |
.overlay { Text("\(idx+1)") } | |
.padding(3) | |
} | |
} | |
.border(.foreground) | |
Spacer() | |
VStack { | |
Slider(value: $viewCount, in: 0...12, step: 1) | |
Text("View count: \(Int(viewCount))") | |
} | |
.padding(.vertical, 20) | |
} | |
.padding() | |
} | |
} | |
struct ComposedLayout: Layout { | |
private let hStack = AnyLayout(HStackLayout(spacing: 0)) | |
private let vStack = AnyLayout(VStackLayout(spacing: 0)) | |
struct Caches { | |
var topCache: AnyLayout.Cache | |
var centerCache: AnyLayout.Cache | |
var bottomCache: AnyLayout.Cache | |
} | |
func makeCache(subviews: Subviews) -> Caches { | |
Caches(topCache: hStack.makeCache(subviews: topViews(subviews: subviews)), | |
centerCache: vStack.makeCache(subviews: centerViews(subviews: subviews)), | |
bottomCache: hStack.makeCache(subviews: bottomViews(subviews: subviews))) | |
} | |
func topViews(subviews: LayoutSubviews) -> LayoutSubviews { | |
return subviews[..<min(subviews.count, 3)] | |
} | |
func centerViews(subviews: LayoutSubviews) -> LayoutSubviews { | |
return subviews.dropFirst(3).dropLast(3) | |
} | |
func bottomViews(subviews: LayoutSubviews) -> LayoutSubviews { | |
if subviews.count < 4 { | |
return subviews.dropLast(subviews.count) // return empty LayoutSubviews | |
} else { | |
return subviews[max(subviews.count - 3, 3)..<subviews.count] | |
} | |
} | |
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Caches) -> CGSize { | |
let tViews = topViews(subviews: subviews) | |
let cViews = centerViews(subviews: subviews) | |
let bViews = bottomViews(subviews: subviews) | |
let tSize = tViews.count == 0 ? .zero : hStack.sizeThatFits(proposal: proposal, subviews: tViews, cache: &cache.topCache) | |
let cSize = cViews.count == 0 ? .zero : vStack.sizeThatFits(proposal: proposal, subviews: cViews, cache: &cache.centerCache) | |
let bSize = bViews.count == 0 ? .zero : hStack.sizeThatFits(proposal: proposal, subviews: bViews, cache: &cache.bottomCache) | |
return CGSize(width: max(tSize.width, max(cSize.width, bSize.width)), | |
height: tSize.height + cSize.height + bSize.height) | |
} | |
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Caches) { | |
let tViews = topViews(subviews: subviews) | |
let cViews = centerViews(subviews: subviews) | |
let bViews = bottomViews(subviews: subviews) | |
var bounds = bounds | |
if tViews.count > 0 { | |
let tSize = hStack.sizeThatFits(proposal: proposal, subviews: tViews, cache: &cache.topCache) | |
hStack.placeSubviews(in: bounds, proposal: proposal, subviews: tViews, cache: &cache.topCache) | |
bounds.origin = CGPoint(x: bounds.origin.x, y: bounds.origin.y + tSize.height) | |
} | |
if cViews.count > 0 { | |
let cSize = vStack.sizeThatFits(proposal: proposal, subviews: cViews, cache: &cache.centerCache) | |
vStack.placeSubviews(in: bounds, proposal: proposal, subviews: cViews, cache: &cache.centerCache) | |
bounds.origin = CGPoint(x: bounds.origin.x, y: bounds.origin.y + cSize.height) | |
} | |
if bViews.count > 0 { | |
hStack.placeSubviews(in: bounds, proposal: proposal, subviews: bViews, cache: &cache.bottomCache) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment