如何在 SwiftUI 中的删除项目上应用更改?

问题描述 投票:0回答:2
设置:

我有一个简单的视图,可以在需要时为其颜色设置动画:

struct SelfAnimatingView: View {
    let name: String
    let animate: Bool

    var body: some View {
        Text(name)
            .padding()
            .foregroundStyle(.white)
            .background(animate ? .red : .blue)
    }
}

以及可移动物品的简单列表:

struct ContentView: View {
    @State private var removable = false
    @State private var names = ["Ted", "Barney", "Lily", "Robin", "Marshal"]
    @State private var animatedNames =  Set<String>()
    private var unanimatedNames: [String] { names.filter({ !animatedNames.contains($0) }) }

    var body: some View {
        VStack {
            Toggle("Removable", isOn: $removable).padding()
            ForEach(removable ? unanimatedNames : names, id: \.self) { name in
                Button { animatedNames.insert(name) } label: {
                    SelfAnimatingView(name: name, animate: animatedNames.contains(name))
                }
            }
            Spacer()
        }
        .animation(.default.delay(1), value: unanimatedNames)
    }
}

行为

SelfAnimatingView
开关按预期关闭时,
Removable
会出现动画:

Demo 1

但是一旦涉及到

delete
逻辑,独立动画就停止工作:

Demo 2

看来 SwiftUI 不会对(逻辑上)已删除的项目应用任何更改。


问题

那么我们如何才能使其应用自包含在被删除之前视觉上发生变化?

animation swiftui
2个回答
0
投票

如果数组

unanimatedNames
是一个状态变量,每次选择一个项目时都会重新构建,而不是将其作为计算属性,那么您就可以让它工作。

  • .onChange
    处理程序中重建它,该处理程序由
    animatedNames
    的更改触发。
  • 在 iOS17 中,您可以使用
    initial
    标志在首次显示时构建它,在以前的 iOS 版本中,可以使用
    .onAppear
    来代替。
struct ContentView: View {
    @State private var removable = false
    @State private var names = ["Ted", "Barney", "Lily", "Robin", "Marshal"]
    @State private var animatedNames = Set<String>()
    @State private var unanimatedNames = [String]()

    var body: some View {
        VStack {
            // content as before
        }
        .animation(.default.delay(1), value: unanimatedNames)
        .onChange(of: animatedNames, initial: true) {
            unanimatedNames = names.filter { !animatedNames.contains($0) }
        }
    }
}

Animation


0
投票

虽然“SwiftUI 不更新已删除的视图”的问题可以通过编写自己的自定义来解决

Transition
,但颜色变化将在其他视图向上移动以填补其位置的同时发生。

您无法控制

ForEach
向上移动其他按钮的动画,与按下的按钮改变其颜色分开。这必须在两个单独的动画中完成。

您可以创建一个自定义

View
来包裹
Button
。这将首先使用单独的
@State
来动画颜色变化,然后才通过调用
animatedNames.insert(name)
将其自身从视图层次结构中删除。

struct CustomButton: View {
    let name: String
    @Binding var names: Set<String>
    @State private var flag = false
    
    var body: some View {
        Button {
            // this guard is not needed in this case, because
            // calling names.insert(name) twice will have the same effect as
            // calling it only once.
            // In general, you need this guard to prevent side effects from
            guard !flag else { return }
            withAnimation {
                flag = true
            } completion: {
                names.insert(name)
            }
        } label: {
            SelfAnimatingView(name: name, animate: flag)
        }
    }
}

用途:

// also consider wrapping this whole if statement in the custom view
if removable {
    CustomButton(name: name, names: $animatedNames)
} else {
    Button { animatedNames.insert(name) } label: {
        SelfAnimatingView(name: name, animate: animatedNames.contains(name))
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.