Swift UI - 为网格堆栈中的单个视图设置动画

Swift UI - Animating individual views in a gridstack

我正在尝试解决以下问题。

我有一个生成视图数组的 GridStack 视图。见下文:

struct GridStack<Content: View> : View {
    let rows :Int
    let columns : Int
    let content : (Int, Int) -> Content
    
    var body: some View {
        VStack{
            ForEach(0 ..< rows){ row in
                HStack{
                    ForEach(0 ..< columns){ column in
                        self.content(row, column)
                    }
                }
            }
        }
    }
}

我目前有这个工作代码,它将数组的值发送到我的主代码中的整数变量 'upTo':

GridStack(rows: 3, columns: 4){ rows, cols in
          Button(action: {
                 upTo = rows * 4 + cols + 1
          }, label: {
                 Text("Number \(rows * 4 + cols + 1)")
          })
}

我想使此文本具有动画效果,但是如果我直接向文本添加动画,则每一个文本都会在我想要 想要设置动画的地方移动我正在点击。

我曾尝试将文本制作成按钮视图并将其传递到主按钮的标签中,但显然嵌入按钮是一种可判处死刑的犯罪行为。

有人有什么想法吗? 提前致谢!

我认为有两种方案可行。请参阅下面我的工作示例。

您希望应用的效果在单击按钮后持续存在吗?在这种情况下,我建议创建一个存储状态的自定义视图。

struct GridStack<Content: View> : View {
    let rows :Int
    let columns : Int
    let content : (Int, Int) -> Content
    
    var body: some View {
        VStack{
            ForEach(0 ..< rows){ row in
                HStack{
                    ForEach(0 ..< columns){ column in
                        self.content(row, column)
                    }
                }
            }
        }
    }
}

struct MyCustomView: View {
    let upTo: Int
    let callback: () -> Void
    
    @State var animate = false
    
    var body: some View {
        Button(action: {
            self.callback()
            self.animate.toggle()
        }) {
            Text("Number \(self.upTo)")
                .background(self.animate ? Color.red : Color.white)
                .animation(.linear(duration: 1.0))
        }
    }
}

struct ContentView2: View {
    @State var upTo: Int = 0
    
    var body: some View {
        VStack {
            Text("\(self.upTo)")

            GridStack(rows: 3, columns: 4) { rows, cols in
                MyCustomView(upTo: rows * 4 + cols + 1, callback: {
                    self.upTo = rows * 4 + cols + 1
                })
            }
        }
    }
}

struct ContentView2_Previews: PreviewProvider {
    static var previews: some View {
        ContentView2()
    }
}

否则自定义按钮样式即可。在这种情况下,您可以访问配置的 isPressed 属性.

struct GridStack<Content: View> : View {
    let rows :Int
    let columns : Int
    let content : (Int, Int) -> Content
    
    var body: some View {
        VStack{
            ForEach(0 ..< rows){ row in
                HStack{
                    ForEach(0 ..< columns){ column in
                        self.content(row, column)
                    }
                }
            }
        }
    }
}

struct MyCustomButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .background(configuration.isPressed ? Color.red : Color.white)
            .animation(.linear(duration: 1.0))
    }
}

struct ContentView2: View {
    @State var upTo: Int = 0
    
    var body: some View {
        VStack {
            Text("\(self.upTo)")

            GridStack(rows: 3, columns: 4) { rows, cols in
                Button(action: {
                    self.upTo = rows * 4 + cols + 1
                }) {
                    Text("Number \(rows * 4 + cols + 1)")
                }
                .buttonStyle(MyCustomButtonStyle())
            }
        }
    }
}

struct ContentView2_Previews: PreviewProvider {
    static var previews: some View {
        ContentView2()
    }
}