在 SwiftUI 中实现标签列表 [英] Implementing a tag list in SwiftUI

查看:29
本文介绍了在 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 的自定义视图,它计算必要的 RowHStack 来放置给定的元素和如果需要,将它们包装成多行.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆