SwiftUI如何在动画完成后触发回调并设置对应属性?
解决方案
iOS 17及以上版本(原生支持动画完成回调)
SwiftUI 从 iOS 17 开始为withAnimation新增了 completion 闭包参数,只需要把动画结束后才要执行的逻辑放到 completion 闭包内,不要放在动画执行闭包里即可。修改后代码如下:
@State var showRect = true @State var showButtonEnabled = false var myTestButton: some View { HStack { Rectangle().fill() .opacity(showRect ? 1 : 0) Button("Show") { withAnimation(.linear(duration: 3)) { showRect = true } completion: { // 动画完全结束后才会执行这段代码 showButtonEnabled = false } }.disabled(!showButtonEnabled) Button("Hide") { withAnimation(.linear(duration: 3)) { showRect = false } completion: { // 动画完全结束后才会执行这段代码 showButtonEnabled = true } }.disabled(showButtonEnabled) } }
原写法中将showButtonEnabled的修改放到了动画执行闭包内,闭包内的所有状态变化会立即触发视图更新,所以按钮状态会立刻改变,和动画时长无关。
iOS 17以下版本兼容方案
如果需要适配更低系统版本,可以通过Transaction添加动画完成回调实现相同效果,代码如下:
@State var showRect = true @State var showButtonEnabled = false var myTestButton: some View { HStack { Rectangle().fill() .opacity(showRect ? 1 : 0) Button("Show") { var transaction = Transaction(animation: .linear(duration: 3)) transaction.addCompletion { _ in showButtonEnabled = false } withTransaction(transaction) { showRect = true } }.disabled(!showButtonEnabled) Button("Hide") { var transaction = Transaction(animation: .linear(duration: 3)) transaction.addCompletion { _ in showButtonEnabled = true } withTransaction(transaction) { showRect = false } }.disabled(showButtonEnabled) } }
注意:不要使用DispatchQueue.main.asyncAfter延迟固定时长的方式修改状态,这种方式在动画被中途打断时会出现状态错误,系统原生的完成回调会自动处理动画打断的场景,逻辑更安全。
内容的提问来源于stack exchange,提问作者Herman
相关产品推荐
相关产品推荐

