0

Based on the value of a @State var, how can I animate any other parameters but one? For instance, I want to animate .offset but not the .foregroundColor. It may sound like an easy task to do, but it's not so easy when using the same "base view" to create some sort of cell views and combining different modifiers and transition.

For the following animation, I'm trying to "avoid" that change-color animation while allowing the offset to happen - I need the change of color to happen instantaneously, no animation.

enter image description here

For this animation, I'm also trying to "avoid" that change-color animation. The challenge here is that the base view also has a transition.

enter image description here

The "base view" name is LogoView. Here's my code:

struct Test_ForegroundColorAnimation: View {
    var array: [Color] = [.yellow, .green, .blue]
    @State var activeIndex1: Int = -1
    @State var activeIndex2: Int = -1
    @State var show: Bool = true

    private func animationFor(index: Int) -> Animation {
        Animation
            .easeInOut(duration: 0.85)
            .delay(0.15 * Double(array.count - index))
    }

    func zindexfor(index: Int) -> Double {
        Double(index == activeIndex1 ? 100 : index)
    }

    func opacityfor(index: Int) -> Double {
        self.activeIndex1 >= 0 ? (index == self.activeIndex1 ? 1 : 0) : 1
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 25) {
            Spacer()

            VStack {
                Text("Offset animation").font(.title)
                Text("Click any item").font(.subheadline)
            }

            ZStack {
                ForEach(array.indices, id: \.self) { index in
                    LogoView(isSelected: index == self.activeIndex1, color: self.array[index])
                        .offset(x: self.activeIndex1 == -1 ? CGFloat(90 * index) : 0)
                        .zIndex(self.zindexfor(index: index))
                        .opacity(self.opacityfor(index: index))
                        .animation(self.animationFor(index: index))
                        .onTapGesture {
                            self.activeIndex1 = index == self.activeIndex1 ? -1 : index
                        }
                }
            }
            .padding(.bottom, 70)

            VStack {
                Text("Transition").font(.title)
                Text("Click any item").font(.subheadline)
            }

            Button(action: {
                self.show.toggle()
            }) {
                Color.purple
                    .frame(width: 100, height: 70)
                    .overlay(Text("Activate transition!").foregroundColor(.white))
            }

            HStack(spacing: 5) {
                ForEach(array.indices, id: \.self) { index in
                    HStack {
                        if self.show {
                            LogoView(isSelected: index == self.activeIndex2, color: self.array[index])
                                .transition(AnyTransition.move(edge: .bottom))
                                .onTapGesture {
                                    self.activeIndex2 = index == self.activeIndex2 ? -1 : index
                                }
                        }
                    }
                    .frame(width: 90, height: 90)
                    .animation(self.animationFor(index: index))
                }
            }
        }
    }
}

struct Test_ForegroundColorAnimation_Previews: PreviewProvider {
    static var previews: some View {
        Test_ForegroundColorAnimation()
    }
}

struct LogoView: View {
    var isSelected: Bool = false
    var color: Color = Color(#colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1))
    var shadow: Color = Color(#colorLiteral(red: 0.4392156899, green: 0.01176470611, blue: 0.1921568662, alpha: 1))

    var body: some View {
        Image(systemName: "heart.fill")
            .resizable()
            .frame(width: 28, height: 28)
            .foregroundColor(isSelected ? Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)) : color)
            .padding()
            .overlay(
                Circle()
                    .stroke(lineWidth: 3)
                    .foregroundColor(color)
            )
            .background(
                Circle()
                    .foregroundColor(isSelected ? color : Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)))
                    .shadow(color: isSelected ? color.opacity(0.7) : Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.3), radius: 5, x: 0, y: 10)
            )
            .frame(width: 90, height: 90)
    }
}
4

1 回答 1

1

如果我正确理解了您的需求,这是一个修复

演示

struct LogoView: View {
    var isSelected: Bool = false
    var color: Color = Color(#colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1))
    var shadow: Color = Color(#colorLiteral(red: 0.4392156899, green: 0.01176470611, blue: 0.1921568662, alpha: 1))

    var body: some View {
        Image(systemName: "heart.fill")
            .resizable()
            .frame(width: 28, height: 28)
            .foregroundColor(isSelected ? Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)) : color)
            .padding()
            .overlay(
                Circle()
                    .stroke(lineWidth: 3)
                    .foregroundColor(color)
            )
            .background(
                Circle()
                    .foregroundColor(isSelected ? color : Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)))
                    .animation(nil)                  // << fix !!
                    .shadow(color: isSelected ? color.opacity(0.7) : Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.3), radius: 5, x: 0, y: 10)
            )
            .frame(width: 90, height: 90)
    }
}
于 2020-04-22T08:23:29.770 回答