在 SwiftUI 中实现标签列表 [英] Implementing a tag list in SwiftUI
问题描述
我正在尝试在 SwiftUI 中实现一个标签列表,但我不确定如果列表水平溢出,如何让它将标签包装到其他行.我从一个名为 tags 的字符串数组开始,然后在 SwiftUI 中循环遍历该数组并创建按钮,如下所示:
I am trying to implement a tag list in SwiftUI but I'm unsure how to get it to wrap the tags to additional lines if the list overflows horizontally. I started with a string array called tags and within SwiftUI I loop through the array and create buttons as follows:
HStack{
ForEach(tags, id: \.self){tag in
Button(action: {}) {
HStack {
Text(tag)
Image(systemName: "xmark.circle")
}
}
.padding()
.foregroundColor(.white)
.background(Color.orange)
.cornerRadius(.infinity)
.lineLimit(1)
}
}
如果标签数组很小,则呈现如下:
If the tags array is small it renders as follows:
然而,如果数组有更多的值,它会这样做:
However, if the array has more values it does this:
我正在寻找的行为是将最后一个标签(黄色)换行到第二行.我意识到它在一个 HStack 中,我希望我可以添加一个大于 1 的值对 lineLimit 的调用,但它似乎并没有改变行为.如果我将外部 HStack 更改为 VStack,它会将每个 Button 放在单独的行上,因此仍然不是我尝试创建的行为.任何指导将不胜感激.
The behavior I am looking for is for the last tag (yellow) to wrap to the second line. I realize it is in an HStack, I was hoping I could add a call to lineLimit with a value of greater than one but it doesn't seem to change the behavior. If I change the outer HStack to a VStack, it puts each Button on a separate line, so still not quite the behavior I am trying create. Any guidance would be greatly appreciated.
推荐答案
Federico Zanetello 在他的文章中分享了一个很好的解决方案博客:SwiftUI 中的灵活布局.
Federico Zanetello shared a nice solution in his blog: Flexible layouts in SwiftUI.
解决方案是一个名为 FlexibleView
的自定义视图,它计算必要的 Row
和 HStack
来放置给定的元素和如果需要,将它们包装成多行.
The solution is a custom view called FlexibleView
which computes the necessary Row
's and HStack
's to lay down the given elements and wrap them into multiple rows if needed.
struct _FlexibleView<Data: Collection, Content: View>: View where Data.Element: Hashable {
let availableWidth: CGFloat
let data: Data
let spacing: CGFloat
let alignment: HorizontalAlignment
let content: (Data.Element) -> Content
@State var elementsSize: [Data.Element: CGSize] = [:]
var body : some View {
VStack(alignment: alignment, spacing: spacing) {
ForEach(computeRows(), id: \.self) { rowElements in
HStack(spacing: spacing) {
ForEach(rowElements, id: \.self) { element in
content(element)
.fixedSize()
.readSize { size in
elementsSize[element] = size
}
}
}
}
}
}
func computeRows() -> [[Data.Element]] {
var rows: [[Data.Element]] = [[]]
var currentRow = 0
var remainingWidth = availableWidth
for element in data {
let elementSize = elementsSize[element, default: CGSize(width: availableWidth, height: 1)]
if remainingWidth - (elementSize.width + spacing) >= 0 {
rows[currentRow].append(element)
} else {
currentRow = currentRow + 1
rows.append([element])
remainingWidth = availableWidth
}
remainingWidth = remainingWidth - (elementSize.width + spacing)
}
return rows
}
}
用法:
FlexibleView(
data: [
"Here’s", "to", "the", "crazy", "ones", "the", "misfits", "the", "rebels", "the", "troublemakers", "the", "round", "pegs", "in", "the", "square", "holes", "the", "ones", "who", "see", "things", "differently", "they’re", "not", "fond", "of", "rules"
],
spacing: 15,
alignment: .leading
) { item in
Text(verbatim: item)
.padding(8)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.gray.opacity(0.2))
)
}
.padding(.horizontal, model.padding)
}
完整代码可在 https://github.com/zntfdr/FiveStarsCodeSamples 获得.
Full code available at https://github.com/zntfdr/FiveStarsCodeSamples.
这篇关于在 SwiftUI 中实现标签列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!