我有一个看法
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
属性更改时,我都希望为从10
到0
的填充设置动画。你能告诉我是否有办法吗?
答案 0 :(得分:1)
您在另一个答案中注意到,您无法从body
内部更新状态。您也无法像使用didSet
那样在@Binding
上使用@State
(至少从Beta 4开始)。
我能想到的最好的解决方案是使用BindableObject
和sink/onReceive
,以便在每次padding
更改时更新color
。我还需要添加一个延迟,以便填充动画结束。
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画布上进行动画处理。
答案 1 :(得分:1)
从Xcode 12开始,Swift 5
达到预期结果的一种方法是将当前选定的索引移到ObservableObject
中。
final class CellViewModel: ObservableObject {
@Published var index: Int
init(index: Int = 0) {
self.index = index
}
}
然后,您的CellView
可以使用.onReceive(_:)
修饰符对索引的这种变化做出反应;使用Publisher
前缀访问@Published
属性包装器提供的$
。
然后可以使用此修饰符提供的闭包来更新填充并为更改添加动画。
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
。
答案 2 :(得分:0)
您可以使用计算属性来使此工作。下面的代码是一个示例示例。
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())
}
}
答案 3 :(得分:0)
只要color属性发生单个增量变化,就会在10到0之间切换填充
padding = color % 2 == 0 ? 10 : 0