![](/img/trans.png)
[英]SwiftUI: react to NSManagedObject property change in SwiftUI view?
[英]Animate view on property change SwiftUI
我有意见
struct CellView: View {
@Binding var color: Int
@State var padding : Length = 10
let colors = [Color.yellow, Color.red, Color.blue, Color.green]
var body: some View {
colors[color]
.cornerRadius(20)
.padding(padding)
.animation(.spring())
}
}
我希望它在属性color
更改时具有填充动画。 我想动画填充从 10 到 0。
我试过使用onAppear
...onAppear {
self.padding = 0
}
但是当视图出现时它只工作一次(按预期),我想每次当属性color
改变时都这样做。 基本上,每次color
属性更改时,我都希望将 padding 从10
为0
。 你能告诉我是否有办法做到这一点?
正如您在另一个答案中注意到的那样,您无法从body
内部更新状态。 你也不能使用didSet
在@Binding
(至少在测试版4)与方式,您可以@State
。
我能想出的最佳解决方案是使用BindableObject
和sink/onReceive
以便在每次color
更改时更新padding
。 我还需要添加延迟以使填充动画完成。
class IndexBinding: BindableObject {
let willChange = PassthroughSubject<Void, Never>()
var index: Int = 0 {
didSet {
self.willChange.send()
}
}
}
struct ParentView: View {
@State var index = IndexBinding()
var body: some View {
CellView(index: self.index)
.gesture(TapGesture().onEnded { _ in
self.index.index += 1
})
}
}
struct CellView: View {
@ObjectBinding var index: IndexBinding
@State private var padding: CGFloat = 0.0
var body: some View {
Color.red
.cornerRadius(20.0)
.padding(self.padding + 20.0)
.animation(.spring())
.onReceive(self.index.willChange) {
self.padding = 10.0
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
self.padding = 0.0
}
}
}
}
此示例不会在 Beta 4 上的 Xcode 画布中设置动画。在模拟器或设备上运行它。
从 Xcode 12 开始,Swift 5
实现预期结果的一种方法是将当前选定的索引移动到ObservableObject
。
final class CellViewModel: ObservableObject {
@Published var index: Int
init(index: Int = 0) {
self.index = index
}
}
然后,您的CellView
可以使用.onReceive(_:)
修饰符对索引的这种变化做出反应; 使用$
前缀访问由@Published
属性包装器提供的Publisher
者。
然后,您可以使用此修饰符提供的闭包来更新填充并为更改设置动画。
struct CellView: View {
@ObservedObject var viewModel: CellViewModel
@State private var padding : CGFloat = 10
let colors: [Color] = [.yellow, .red, .blue, .green]
var body: some View {
colors[viewModel.index]
.cornerRadius(20)
.padding(padding)
.onReceive(viewModel.$index) { _ in
padding = 10
withAnimation(.spring()) {
padding = 0
}
}
}
}
这是用于演示的示例父视图:
struct ParentView: View {
let viewModel: CellViewModel
var body: some View {
VStack {
CellView(viewModel: viewModel)
.frame(width: 200, height: 200)
HStack {
ForEach(0..<4) { i in
Button(action: { viewModel.index = i }) {
Text("\(i)")
.padding()
.frame(maxWidth: .infinity)
.background(Color(.secondarySystemFill))
}
}
}
}
}
}
请注意,此处的 Parent 不需要其 viewModel 属性为@ObservedObject
。
您可以使用 Computed Properties 使其正常工作。 下面的代码是如何完成的示例。
import SwiftUI
struct ColorChanges: View {
@State var color: Float = 0
var body: some View {
VStack {
Slider(value: $color, from: 0, through: 3, by: 1)
CellView(color: Int(color))
}
}
}
struct CellView: View {
var color: Int
@State var colorOld: Int = 0
var padding: CGFloat {
if color != colorOld {
colorOld = color
return 40
} else {
return 0
}
}
let colors = [Color.yellow, Color.red, Color.blue, Color.green]
var body: some View {
colors[color]
.cornerRadius(20)
.padding(padding)
.animation(.spring())
}
}
每当颜色属性发生单个增量更改时,这将在 10 和 0 之间切换填充
padding = color % 2 == 0 ? 10 : 0
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.