订阅合并发布服务器的每个更新的SwiftUI视图
下面是一个我想采用的方法的简化示例,但我无法让这个简单的示例起作用 我有一个联合发布者,其主题是视图模型状态:订阅合并发布服务器的每个更新的SwiftUI视图,swiftui,combine,Swiftui,Combine,下面是一个我想采用的方法的简化示例,但我无法让这个简单的示例起作用 我有一个联合发布者,其主题是视图模型状态: struct State { let a: Bool let b: Bool let transition: Transition? } 状态包括一个转换属性。这描述了状态为了成为当前状态而进行的转换 enum Transition { case onAChange, onBChange } 我想使用transition属性在订阅给发布者的视图中驱动
struct State {
let a: Bool
let b: Bool
let transition: Transition?
}
状态包括一个转换
属性。这描述了状态
为了成为当前状态而进行的转换
enum Transition {
case onAChange, onBChange
}
我想使用transition
属性在订阅给发布者的视图中驱动动画,以便不同的转场以特定的方式进行动画制作
视图代码
下面是视图的代码。您可以看到它如何尝试使用转换
选择要更新的动画
struct TestView: View {
let model: TestViewModel
@State private var state: TestViewModel.State
private var cancel: AnyCancellable?
init(model: TestViewModel) {
self.model = model
self._state = State(initialValue: model.state.value)
self.cancel = model.state.sink(receiveValue: updateState(state:))
}
var body: some View {
VStack(spacing: 20) {
Text("AAAAAAA").scaleEffect(state.a ? 2 : 1)
Text("BBBBBBB").scaleEffect(state.b ? 2 : 1)
}
.onTapGesture {
model.invert()
}
}
private func updateState(state: TestViewModel.State) {
withAnimation(animation(for: state.transition)) {
self.state = state
}
}
private func animation(for transition: TestViewModel.Transition?) -> Animation? {
guard let transition = transition else { return nil }
switch transition {
case .onAChange: return .easeInOut(duration: 1)
case .onBChange: return .easeInOut(duration: 2)
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView(model: TestViewModel())
}
}
模型代码
final类TestViewModel:ObserveObject{
var状态=CurrentValueSubject(状态(a:false,b:false,转换:nil))
结构状态{
让a:布尔
b:布尔
让过渡:过渡?
}
枚举转换{
改变的情况
}
func invert(){
让oldState=state.value
setState(newState:.init(a:!oldState.a,b:oldState.b,转换:.onChange))
setState(newState:.init(a:!oldState.a,b:!oldState.b,转换:.onBChange))
}
私有函数集状态(新闻状态:状态){
state.value=newState
}
}
您可以在模型代码中看到,当调用invert()
时,会发生两种状态更改。模型首先使用.onchange
转换切换a
,然后使用.onBChange
转换切换b
该怎么办
运行此操作时,每次单击视图时,文本“AAAAAAA”和“BBBBBBB”应切换大小。但是,“AAAAAAA”文本应快速更改(1秒),而“BBBBBBB”文本应缓慢更改(2秒)
到底发生了什么
但是,当我运行此命令并单击视图时,视图根本不会更新
我可以从调试器中看到,在模型上调用了ontapposition{…}
,并且调用了invert()
。还将调用updateState(状态:)
。但是,TestView
不会在屏幕上更改,并且不会再次调用body
我试过的其他东西
使用回调
我没有使用发布者将事件发送到视图,而是在模型集中尝试了一个回调函数,该函数设置为视图的updateState(state:)
函数。我用model.handleUpdate=self.update(状态:)
在视图的init
中对此进行了分配。同样,这没有起作用。函数invert()
和update(state:)
按预期被调用,但视图实际上没有改变
使用@ObservedObject
我将模型更改为可观察对象
,其状态
为@已发布
。我将视图设置为具有模型的@observedObject
。这样,视图确实会更新,但它会使用相同的动画更新两段文本,这是我不想要的。这两个状态更新似乎被压缩了,它只看到最后一个状态更新,并从中使用转换
一些确实有效的东西——某种程度上
最后,我尝试将模型的invert()
函数直接复制到视图的ontapsignate
处理程序中,以便视图直接更新自己的状态。这确实有效!这很重要,但我不想把所有的模型更新逻辑放在我的视图中
问题:
如何让SwiftUI视图订阅模型通过其发布服务器发送的所有状态,以便它可以使用状态中的转换属性来控制用于该状态更改的动画?订阅发布服务器视图的方式是使用.onReceive(uuu2;: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
}
var主体:一些视图{
VStack(间距:20){
文本(“AAAAAAA”).scaleEffect(state.a?2:1)
文本(“bbb”).scaleEffect(state.b?2:1)
}
.ontapsigne{
model.invert()
}
.onReceive(model.state,perform:updateState(state:)/向发布者订阅视图的方式是使用。onReceive(uquo: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
}
var主体:一些视图{
VStack(间距:20){
文本(“AAAAAAA”).scaleEffect(state.a?2:1)
文本(“bbb”).scaleEffect(state.b?2:1)
}
.ontapsigne{
model.invert()
}
.onReceive(model.state,perform:updateState(state:)//这很管用!棒极了–我完全以为我试过了,结果发现它不管用(它只更新了最终状态,但只使用了一个动画).谢谢你…你能扩展你的答案来解释为什么其他方法不起作用吗?要么什么都不做,要么只更新最终状态?我还没有深入分析你的其他方法,但你应该记住,视图只是一个结构,SwiftUI可以复制它并重新初始化它,不管它想要多少次,所以在init中完成的订阅可能会发生在TestView
的某个副本上,而不是视图层次结构中的副本。在init之后(或直接使用self.\u prop
)必须将值分配给@State
属性。这很有效!太棒了–我完全认为我已经尝试过了,但发现它不起作用(它只更新了如此更改的最终状态,但只使用了一个动画).谢谢你…你能扩展你的答案来解释为什么其他方法不起作用吗?要么什么都不做,要么只更新最终状态?我还没有深入分析你的其他方法,但你应该记住,视图只是一个结构,SwiftUI可以复制它并重新初始化它,不管它想要多少次,所以订阅完成