![](/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.