Is it possible that the blue tags (which are currently truncated) are displayed completely and then it automatically makes a line break?
NavigationLink(desti
I have something like this code (rather long). In simple scenarios it works ok, but in deep nesting with geometry readers it doesn't propagate its size well.
It would be nice if this views wraps and flows like Text() extending parent view content, but it seems to have explicitly set its height from parent view.
https://gist.github.com/michzio/a0b23ee43a88cbc95f65277070167e29
Here is the most important part of the code (without preview and test data)
[![private func flow(in geometry: GeometryProxy) -> some View {
print("Card geometry: \(geometry.size.width) \(geometry.size.height)")
return ZStack(alignment: .topLeading) {
//Color.clear
ForEach(data, id: self.dataId) { element in
self.content(element)
.geometryPreference(tag: element\[keyPath: self.dataId\])
/*
.alignmentGuide(.leading) { d in
print("Element: w: \(d.width), h: \(d.height)")
if (abs(width - d.width) > geometry.size.width)
{
width = 0
height -= d.height
}
let result = width
if element\[keyPath: self.dataId\] == self.data.last!\[keyPath: self.dataId\] {
width = 0 //last item
} else {
width -= d.width
}
return result
}
.alignmentGuide(.top) { d in
let result = height
if element\[keyPath: self.dataId\] == self.data.last!\[keyPath: self.dataId\] {
height = 0 // last item
}
return result
}*/
.alignmentGuide(.top) { d in
self.alignmentGuides\[element\[keyPath: self.dataId\]\]?.y ?? 0
}
.alignmentGuide(.leading) { d in
self.alignmentGuides\[element\[keyPath: self.dataId\]\]?.x ?? 0
}
}
}
.background(Color.pink)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
//.animation(self.loaded ? .linear(duration: 1) : nil)
.onPreferenceChange(_GeometryPreferenceKey.self, perform: { preferences in
DispatchQueue.main.async {
let (alignmentGuides, totalHeight) = self.calculateAlignmentGuides(preferences: preferences, geometry: geometry)
self.alignmentGuides = alignmentGuides
self.totalHeight = totalHeight
self.availableWidth = geometry.size.width
}
})
}
func calculateAlignmentGuides(preferences: \[_GeometryPreference\], geometry: GeometryProxy) -> (\[AnyHashable: CGPoint\], CGFloat) {
var alignmentGuides = \[AnyHashable: CGPoint\]()
var width: CGFloat = 0
var height: CGFloat = 0
var rowHeights: Set<CGFloat> = \[\]
preferences.forEach { preference in
let elementWidth = spacing + preference.rect.width
if width + elementWidth >= geometry.size.width {
width = 0
height += (rowHeights.max() ?? 0) + spacing
//rowHeights.removeAll()
}
let offset = CGPoint(x: 0 - width, y: 0 - height)
print("Alignment guides offset: \(offset)")
alignmentGuides\[preference.tag\] = offset
width += elementWidth
rowHeights.insert(preference.rect.height)
}
return (alignmentGuides, height + (rowHeights.max() ?? 0))
}
}][1]][1]
Here is some approach of how this could be done using alignmentGuide(s). It is simplified to avoid many code post, but hope it is useful.
Update: There is also updated & improved variant of below solution in my answer for SwiftUI HStack with wrap and dynamic height
This is the result:
And here is full demo code (orientation is supported automatically):
import SwiftUI
struct TestWrappedLayout: View {
@State var platforms = ["Ninetendo", "XBox", "PlayStation", "PlayStation 2", "PlayStation 3", "PlayStation 4"]
var body: some View {
GeometryReader { geometry in
self.generateContent(in: geometry)
}
}
private func generateContent(in g: GeometryProxy) -> some View {
var width = CGFloat.zero
var height = CGFloat.zero
return ZStack(alignment: .topLeading) {
ForEach(self.platforms, id: \.self) { platform in
self.item(for: platform)
.padding([.horizontal, .vertical], 4)
.alignmentGuide(.leading, computeValue: { d in
if (abs(width - d.width) > g.size.width)
{
width = 0
height -= d.height
}
let result = width
if platform == self.platforms.last! {
width = 0 //last item
} else {
width -= d.width
}
return result
})
.alignmentGuide(.top, computeValue: {d in
let result = height
if platform == self.platforms.last! {
height = 0 // last item
}
return result
})
}
}
}
func item(for text: String) -> some View {
Text(text)
.padding(.all, 5)
.font(.body)
.background(Color.blue)
.foregroundColor(Color.white)
.cornerRadius(5)
}
}
struct TestWrappedLayout_Previews: PreviewProvider {
static var previews: some View {
TestWrappedLayout()
}
}
You need to handle line configurations right after Text View. Don't use lineLimit(1) if you need multiple lines.
HStack(alignment: .top, spacing: 10){
ForEach(collection.platforms, id: \.self) { platform in
Text(platform)
.fixedSize(horizontal: false, vertical: true)
.lineLimit(10)
.multilineTextAlignment(.leading)
.padding(.all, 5)
.font(.caption)
.background(Color.blue)
.foregroundColor(Color.white)
.cornerRadius(5)
}
}
I found a Framework-Solution called ASCollectionView. I hope this helps someone who has the same problem.