0

基本上,我有一个我想内联显示的标签列表,但是当标签很多并且它们的总宽度超过父视图的宽度时,我想显示一个“+n”徽章。

期望的行为

如何从这个基本代码开始实现所需的行为?

struct ParentView: View {
   var body: some View {
      HStack {
         Tag("Apple")
         Tag("Banana")
         Tag("Cherry")
         // more tags...
      }
      .frame(width: 200, alignment: .leading)
   }
}

有什么建议么?提前致谢!

4

1 回答 1

2

1.

找到每个徽章的宽度。

width of badge = string's width + padding-x

2.

使用这个扩展来查找字符串的宽度

extension String {
   func widthOfString(usingFont font: UIFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)) -> CGFloat {
        let fontAttributes = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttributes)
        return size.width
    }
}

我已经取消了 iOS 中的默认字体,因为我没有更改 Text 的字体。

3.

计算小于屏幕宽度的徽章总宽度。



struct ContentView: View {
    let one = ["One"]
    let two = ["One", "Two"]
    let three = ["One", "Two", "Three"]
    let four = ["One", "Two", "Three", "Four"]
    let five = ["One", "Two", "Three", "Four", "Five"]
    let six = ["One", "Two", "Three", "Four", "Five", "Six"]
    let fruits = ["Apple",  "Banana", "Orange", "Cherry", "Kiwi"]
    let countries = ["France",  "Spain", "Banana Republic", "USA", "Albania", "China", "England"]
    let cities = ["Madrid",  "Oslo", "Washington DC", "Istanbul", "Toronto", "Paris"]
    
    let screenWidth = UIScreen.main.bounds.width // use another number if needed
    
    var body: some View  {
        VStack(alignment: .leading) {
            TagsView(tags: one, screenWidth: screenWidth)
            TagsView(tags: two, screenWidth: screenWidth)
            TagsView(tags: three, screenWidth: screenWidth)
            TagsView(tags: four, screenWidth: screenWidth)
            TagsView(tags: five, screenWidth: screenWidth)
            TagsView(tags: six, screenWidth: screenWidth)
            TagsView(tags: fruits, screenWidth: screenWidth)
            TagsView(tags: countries, screenWidth: screenWidth)
            TagsView(tags: cities, screenWidth: screenWidth)
        }
        
    }
}


struct TagsView: View {
    let spacing: CGFloat = 8
    let padding: CGFloat = 16
    let tags: [String]
    var width: CGFloat = .zero
    var limit = 0
    
    internal init(tags: [String], screenWidth: CGFloat) {
        self.tags = tags
        self.limit = tags.count
        
        for (index, tag) in tags.enumerated()  {
            self.width += tag.widthOfString() + (2 * padding) +  spacing
            let remaining = "\(tags.count - index) +".widthOfString() + (2 * padding)
            if width + remaining >= screenWidth {
                self.limit = index
                break
            }
        }
    }

    var body: some View {
        HStack(spacing: spacing) {
            ForEach(0..<limit, id: \.self) { index in
                Tag(padding: padding, text: tags[index])
            }
            
            if limit > 0 && tags.count != limit {
                Tag(padding: padding, text: "\(tags.count - limit) +")
            }
        }
        .padding(.bottom)
    }
}

struct Tag:View {
    let padding: CGFloat
    let text: String
    let height: CGFloat = 20
    let pVertical:CGFloat = 8
    var body: some View {
        Text(text)
            .fixedSize()
            .padding(.horizontal, padding)
            .padding(.vertical, pVertical)
            .background(Color.orange.opacity(0.2))
            .foregroundColor(Color.orange)
            .cornerRadius( (height + pVertical * 2) / 2)
            .frame(height: height)
    }
}


extension String {
   func widthOfString(usingFont font: UIFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)) -> CGFloat {
        let fontAttributes = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttributes)
        return size.width
    }
}


在黑暗的外观

在此处输入图像描述

于 2021-09-21T12:56:03.263 回答