这是我想要采用的方法的简化示例,但我无法让这个简单的示例发挥作用。
\n我有一个组合发布者,其主题是视图模型状态:
\nstruct State {\n let a: Bool\n let b: Bool\n let transition: Transition?\n}\n
Run Code Online (Sandbox Code Playgroud)\n国家包括transition
财产。这描述了为了成为当前状态而做出的Transition
事情。State
enum Transition {\n case onAChange, onBChange\n}\n
Run Code Online (Sandbox Code Playgroud)\n我想使用transition
属性来驱动订阅发布者中的动画View
,以便不同的过渡以特定的方式进行动画处理。
这是视图的代码。您可以看到它如何尝试使用 选择transition
要更新的动画。
struct TestView: View {\n \n let model: TestViewModel\n\n @State private var state: TestViewModel.State\n private var cancel: AnyCancellable?\n\n init(model: TestViewModel) {\n self.model = model\n self._state = State(initialValue: model.state.value)\n self.cancel = model.state.sink(receiveValue: updateState(state:))\n }\n \n var body: some View {\n VStack(spacing: 20) {\n Text("AAAAAAA").scaleEffect(state.a ? 2 : 1)\n Text("BBBBBBB").scaleEffect(state.b ? 2 : 1)\n }\n .onTapGesture {\n model.invert()\n }\n }\n \n private func updateState(state: TestViewModel.State) {\n withAnimation(animation(for: state.transition)) {\n self.state = state\n }\n }\n\n private func animation(for transition: TestViewModel.Transition?) -> Animation? {\n guard let transition = transition else { return nil }\n\n switch transition {\n case .onAChange: return .easeInOut(duration: 1)\n case .onBChange: return .easeInOut(duration: 2)\n }\n }\n}\n\nstruct TestView_Previews: PreviewProvider {\n static var previews: some View {\n TestView(model: TestViewModel())\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\nfinal class TestViewModel: ObservableObject {\n \n var state = CurrentValueSubject<State, Never>(State(a: false, b: false, transition: nil))\n \n struct State {\n let a: Bool\n let b: Bool\n let transition: Transition?\n }\n \n enum Transition {\n case onAChange, onBChange\n }\n\n func invert() {\n let oldState = state.value\n setState(newState: .init(a: !oldState.a, b: oldState.b, transition: .onAChange))\n setState(newState: .init(a: !oldState.a, b: !oldState.b, transition: .onBChange))\n }\n \n private func setState(newState: State) {\n state.value = newState\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n在模型代码中可以看到,当invert()
调用时,会发生两次状态变化。模型首先a
使用.onAChange
过渡进行切换,然后b
使用.onBChange
过渡进行切换。
运行时应该发生的情况是,每次单击视图时,文本“AAAAAAA”和“BBBBBBB”都应该切换大小。但是,“AAAAAAA”文本应快速变化(1 秒),而“BBBBBBB”文本应缓慢变化(2 秒)。
\n但是,当我运行此命令并单击视图时,视图根本不更新。
\n我可以从模型上onTapGesture { \xe2\x80\xa6 }
调用和正在调用的调试器中看到。invert()
也updateState(state:)
正被称为。但是,TestView
屏幕上并没有发生变化,也body
不会再次调用。
我没有使用发布者将事件发送到视图,而是尝试在模型中将回调函数设置为视图的updateState(state:)
函数。init
我在视图中分配给了这个model.handleUpdate = self.update(state:)
。同样,这不起作用。正如预期的那样,函数invert()
和update(state:)
被调用,但视图实际上并没有改变。
@ObservedObject
我将模型更改ObservableObject
为其state
存在@Published
。@ObservedOject
我设置了模型的视图。这样,视图确实会更新,但它使用相同的动画更新两段文本,这是我不想要的。似乎两个状态更新被压缩,它只看到最后一个,并使用其中的transition
。
最后,我尝试直接将模型的invert()
函数复制到视图的onTapGesture
处理程序中,以便视图直接更新自己的状态。这确实有效!这很重要,但我不想将所有模型更新逻辑都放在我的视图中。
如何让 SwiftUI 视图订阅模型通过其发布者发送的所有transition
状态,以便它可以使用状态中的属性来控制用于该状态更改的动画?
向发布者订阅视图的方式是使用.onRecieve(_:perform:)
,因此不要在 中保存可取消的内容init
,而是执行以下操作:
var body: some View {
VStack(spacing: 20) {
Text("AAAAAAA").scaleEffect(state.a ? 2 : 1)
Text("BBBBBBB").scaleEffect(state.b ? 2 : 1)
}
.onTapGesture {
model.invert()
}
.onReceive(model.state, perform: updateState(state:)) // <- here
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3269 次 |
最近记录: |