如何处理 SwiftUI 中列表行上多个按钮的动画?

问题描述 投票:0回答:1

Button
内的
HStack
内使用多个
List
时,我有一个奇怪的动画行为。首先,我发现我需要使用
.automatic
以外的某些按钮样式才能使两个按钮在单行中正常工作。
这似乎效果很好,直到我想向这些按钮添加动画。我想在行的每一侧创建两个按钮,但仅当满足条件时才会显示第二个按钮,这可以通过单击第一个按钮来实现。简单的例子解释看起来像这样:

struct TestView: View {
    @State
    private var flag1 = true
    @State
    private var flag2 = false
    
    var body: some View {
        NavigationStack {
            List {
                HStack {
                    Button {
                        withAnimation {
                            flag1.toggle()
                        }
                    } label: {
                        Image(systemName: flag1 ? "checkmark.circle" : "circle")
                    }
                    Text("Some text")
                    if flag1 {
                        Spacer()
                        Button {
                            withAnimation {
                                flag2.toggle()
                            }
                        } label: {
                            Image(systemName: flag2 ? "checkmark.circle" : "circle")
                        }
                    }
                }
                .buttonStyle(.borderless)
            }
        }
    }
}

上面的代码会导致这种奇怪的动画行为:

当我删除按钮样式修改器(

.buttonStyle(.borderless)
)时,动画效果更好,但仍然不理想。当第二个按钮应该隐藏时,有用于过渡的动画,但有时会出现动画,有时则没有动画。最重要的是,在这种情况下,两个按钮操作会同时触发:

我发现的最佳解决方案是将按钮替换为

.onTapGesture
修饰符:

struct TestView: View {
    @State
    private var flag1 = true
    @State
    private var flag2 = false

    var body: some View {
        NavigationStack {
            List {
                HStack {
                    Image(systemName: flag1 ? "checkmark.circle" : "circle")
                        .onTapGesture {
                            withAnimation {
                                flag1.toggle()
                            }
                        }
                    Text("Some text")
                    if flag1 {
                        Spacer()
                        Image(systemName: flag2 ? "checkmark.circle" : "circle")
                            .onTapGesture {
                                withAnimation {
                                    flag2.toggle()
                                }
                            }
                    }
                }
            }
        }
    }
}

然而,使用我在按钮格式和第二个按钮出现的动画上松散的内容仍然是随机的。所以,这是我的问题:

  • 有没有一种方法可以在一个
    Button
    行上放置 2 个
    List
    而不会出现动画问题?
  • 为什么第二个按钮的过渡动画会随机出现(当我使用
    .buttonStyle(.borderless)
    或使用
    .onTapGesture
    时?
  • 我使用
    .onTapGesture
    而不是
    Button
    会失去很多吗?如何以看起来像(并保持这种方式)正常
    Button
    的方式格式化组件?
ios swift animation swiftui
1个回答
0
投票
In regards to inconsistencies in animation behavior, consider explicitly specifying the transition for the second button's appearance? 

In regards to button and onTap... onTapGesture is more flexible but you will have to manage animations and state changes manually.

Just to be clear.. when you click into the "Some text" circle you want the second button to appear visible and with the checkmark indicator?

I achieved the above description by using a Timer to control the appearance of a checkmark inside the second button after a delay, allowing for more control over the animation. I tested the solution below on an actual device (not the simulator), and it seems to be working as expected.

import SwiftUI

struct TestView: View {
    @State private var flag1 = true
    @State private var flag2 = false

    var body: some View {
        NavigationStack {
            List {
                HStack {
                    Image(systemName: flag1 ? "checkmark.circle" : "circle")
                        .onTapGesture {
                            withAnimation {
                                flag1.toggle()
                                
                                // Toggle flg2 after 5 second delay
                                DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
                                    withAnimation {
                                        flag2 = true // Set flg 2 to true
                                    }
                                }
                            }
                        }
                    Text("Some text")
                    if flag1 {
                        Spacer()
                        Image(systemName: flag2 ? "checkmark.circle" : "circle")
                           
                    }
                }
            }
        }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        TestView()
    }
}
... 
© www.soinside.com 2019 - 2024. All rights reserved.