类似 SwiftUI FlowLayout 的视图溢出 VStack 中的其他视图

SwiftUI FlowLayout-like View overflowing over other view in VStack

我在 Whosebug 上发现了一些关于创建灵活布局 (FlowLayout) 的问题,比如通常显示标签,根据文本使用不同的按钮宽度,必要时在多行上显示。

代码是这样的:

@State var buttonStrings:[String]=[String]()


init()
{
    self.buttonStrings=createStrings()
}


private func item(for text: String) -> some View {
    
Button(action:{doSomething()})
{
Text(text)
}

}

private func generateContent(in g: GeometryProxy) -> some View {
        var width = CGFloat.zero
        var height = CGFloat.zero

        return ZStack(alignment: .topLeading) {
            ForEach(self.buttonStrings, id: \.self) { string in
                self.item(for: string)
                    .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 string == self.buttonStrings.last! {
                            width = 0 //last item
                        } else {
                            width -= d.width
                        }
                        return result
                    })
                    .alignmentGuide(.top, computeValue: {d in
                        let result = height
                        if string == self.buttonStrings.last! {
                            height = 0 // last item
                        }
                        return result
                    })
            }
        }
    }

它有效,但是当这种视图在 VStack 内并且另一个视图紧随其后时,它会垂直溢出到另一个视图上(或者根据布局配置和 Stacks 的存在将其驱逐到底部) .

如何避免这种情况并且灵活的视图不会溢出或太大? 事实上,它里面的视图被置换了,这导致了这个问题。

使用该代码的正确方法是

struct FlowLayoutLikeView:View
{
    var geometry:GeometryProxy
    @State var buttonStrings:[String]=[String]()

    init(geometry:GeometryProxy,....)
    {
    self.geometry=geometry
    ...
    ...
    }

var body: some View {

        self.generateContent(in: geometry)
           
    }

private func item(for text: String) -> some View {

    Button(action:{doSomething()})
    {
        Text(text)
    }

}

private func generateContent(in g: GeometryProxy) -> some View {
    var width = CGFloat.zero
    var height = CGFloat.zero

    return ZStack(alignment: .topLeading) {
        ForEach(self.buttonStrings, id: \.self) { string in
            self.item(for: string)
                .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 string == self.buttonStrings.last! {
                        width = 0 //last item
                    } else {
                        width -= d.width
                    }
                    return result
                })
                .alignmentGuide(.top, computeValue: {d in
                    let result = height
                    if string == self.buttonStrings.last! {
                        height = 0 // last item
                    }
                    return result
                })
        }
    }
}

包含的视图必须像

var body: some View {
    VStack //this does the trick, do not remove
    {

        GeometryReader { geometry in
        
            VStack //this also does the trick, do not remove
            {
            FlowLayoutLikeView(geometry)

            AnotherView() //this will be fine, it will be placed below the FlowLayoutLikeView, no overlapping, overflowing or ousting

            }
        }
    }
}