Skip to content

Instantly share code, notes, and snippets.

@robhasacamera
Last active June 2, 2022 16:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robhasacamera/379fe5a88fc5bd5cbfddd1994fe5b96a to your computer and use it in GitHub Desktop.
Save robhasacamera/379fe5a88fc5bd5cbfddd1994fe5b96a to your computer and use it in GitHub Desktop.
import SwiftUI
// Adapted from: https://stackoverflow.com/questions/62102647/swiftui-hstack-with-wrap-and-dynamic-height/62103264#62103264
struct WrappingHStack<Model, V>: View where Model: Hashable, V: View {
typealias ViewGenerator = (Model) -> V
var models: [Model]
var viewGenerator: ViewGenerator
var horizontalSpacing: CGFloat = 2
var verticalSpacing: CGFloat = 0
@State private var totalHeight
= CGFloat.zero // << variant for ScrollView/List
// = CGFloat.infinity // << variant for VStack
var body: some View {
VStack {
GeometryReader { geometry in
self.generateContent(in: geometry)
}
}
.frame(height: totalHeight)// << variant for ScrollView/List
//.frame(maxHeight: totalHeight) // << variant for VStack
}
private func generateContent(in geometry: GeometryProxy) -> some View {
var width = CGFloat.zero
var height = CGFloat.zero
return ZStack(alignment: .topLeading) {
ForEach(self.models, id: \.self) { models in
viewGenerator(models)
.padding(.horizontal, horizontalSpacing)
.padding(.vertical, verticalSpacing)
.alignmentGuide(.leading, computeValue: { dimension in
if (abs(width - dimension.width) > geometry.size.width)
{
width = 0
height -= dimension.height
}
let result = width
if models == self.models.last! {
width = 0 //last item
} else {
width -= dimension.width
}
return result
})
.alignmentGuide(.top, computeValue: {dimension in
let result = height
if models == self.models.last! {
height = 0 // last item
}
return result
})
}
}.background(viewHeightReader($totalHeight))
}
private func viewHeightReader(_ binding: Binding<CGFloat>) -> some View {
return GeometryReader { geometry -> Color in
let rect = geometry.frame(in: .local)
DispatchQueue.main.async {
binding.wrappedValue = rect.size.height
}
return .clear
}
}
}
@skywalkerlw
Copy link

This is great.
how can I make align right, rather than with left?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment