Warm tip: This article is reproduced from serverfault.com, please click

Swift UI

发布于 2020-11-28 15:20:06

I'm trying to solve the following problem.

I have a GridStack view which generates an array of views. See below:

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)
                    }
                }
            }
        }
    }
}

I currently have this working code which sends the array's value into the integer variable 'upTo' in my main code:

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

I would like to make this Text animate however if I add animations to the Text directly, every one of the texts moves on tapping where I would only like to animate the one that I am clicking.

I have tried to make the text into a Button view and pass it into the main button's label but apparently embedding buttons is a crime punishable by death.

Does anyone have any ideas? Thanks in advance!

Questioner
lulubasher
Viewed
0
Nikolai 2020-12-01 01:58:22

I think two solutions are feasible. Please see my working examples below.

Would you like the applied effect to persist after clicking the button? In this case, I would recommend to create a custom view which stores the state.

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()
    }
}

Otherwise a custom button style will do. In this case you are able to access the configuration's isPressed property.

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()
    }
}