Skip to content

Instantly share code, notes, and snippets.

@peterwarbo
Created August 6, 2022 08:11
Show Gist options
  • Save peterwarbo/7c3208a4f142b17d095b540567c67ce1 to your computer and use it in GitHub Desktop.
Save peterwarbo/7c3208a4f142b17d095b540567c67ce1 to your computer and use it in GitHub Desktop.
Comparing custom HStack with default HStack
//: A UIKit based Playground for presenting user interface
import SwiftUI
import PlaygroundSupport
struct MyEqualWidthHStack: Layout {
/// Returns a size that the layout container needs to arrange its subviews
/// horizontally.
/// - Tag: sizeThatFitsHorizontal
func sizeThatFits(
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout Void
) -> CGSize {
guard !subviews.isEmpty else { return .zero }
let maxSize = maxSize(subviews: subviews)
let spacing = spacing(subviews: subviews)
let totalSpacing = spacing.reduce(0) { $0 + $1 }
return CGSize(
width: maxSize.width * CGFloat(subviews.count) + totalSpacing,
height: maxSize.height)
}
/// Places the subviews in a horizontal stack.
/// - Tag: placeSubviewsHorizontal
func placeSubviews(
in bounds: CGRect,
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout Void
) {
guard !subviews.isEmpty else { return }
let maxSize = maxSize(subviews: subviews)
let spacing = spacing(subviews: subviews)
let placementProposal = ProposedViewSize(width: maxSize.width, height: maxSize.height)
var nextX = bounds.minX + maxSize.width / 2
for index in subviews.indices {
subviews[index].place(
at: CGPoint(x: nextX, y: bounds.midY),
anchor: .center,
proposal: placementProposal)
nextX += maxSize.width + spacing[index]
}
}
/// Finds the largest ideal size of the subviews.
private func maxSize(subviews: Subviews) -> CGSize {
let subviewSizes = subviews.map { $0.sizeThatFits(.unspecified) }
let maxSize: CGSize = subviewSizes.reduce(.zero) { currentMax, subviewSize in
CGSize(
width: max(currentMax.width, subviewSize.width),
height: max(currentMax.height, subviewSize.height))
}
return maxSize
}
/// Gets an array of preferred spacing sizes between subviews in the
/// horizontal dimension.
private func spacing(subviews: Subviews) -> [CGFloat] {
subviews.indices.map { index in
guard index < subviews.count - 1 else { return 0 }
return subviews[index].spacing.distance(
to: subviews[index + 1].spacing,
along: .horizontal)
}
}
}
struct ContentView: View {
var body: some View {
VStack(spacing: 0) {
(MyEqualWidthHStack()) {
Text("Hi")
.frame(maxWidth: .infinity)
.background(.blue)
Text("Hellooooo")
.frame(maxWidth: .infinity)
.background(.green)
Text("World")
.frame(maxWidth: .infinity)
.background(.orange)
}
.padding(.vertical)
.padding(.bottom, 10)
.font(.headline)
Divider()
HStack {
Text("Hi")
.frame(maxWidth: .infinity)
.background(.blue)
Text("Hellooooo")
.frame(maxWidth: .infinity)
.background(.green)
Text("World")
.frame(maxWidth: .infinity)
.background(.orange)
}
.padding(.vertical)
.padding(.bottom, 10)
.font(.headline)
}
.background(.thinMaterial)
}
}
let hostingVc = UIHostingController(rootView: ContentView())
hostingVc.view.frame = .init(x: 0, y: 0, width: 500, height: 600)
PlaygroundPage.current.liveView = hostingVc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment