You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI多ObservableObject架构实现手指计数器及联动逻辑问题

解决方案:主模型统一管控状态联动,彻底规避子模型依赖问题

核心思路:把所有业务逻辑全丢给主模型,子模型只负责存状态、发变更通知,完全做“哑巴”。既解决之前的无限循环问题,又能砍掉Combine监听的额外开销,还适配你提到的双视频同步场景。

1. 先定义纯哑子模型(左右手)

只负责存储计数,连加减逻辑都没有,就一个简单的设置方法:

import SwiftUI
import Combine

class Hand: ObservableObject {
    @Published var count: Int = 0
    
    // 仅提供基础赋值,无任何业务逻辑
    func setCount(_ value: Int) {
        count = value
    }
}

2. 主模型:所有逻辑的中枢

主模型持有左右手实例,对外提供操作方法,进位逻辑全在这里处理,所有状态变更都由它主动触发:

class FingerCounterModel: ObservableObject {
    let leftHand = Hand()
    let rightHand = Hand()
    
    // 左手加1,主模型统一处理进位
    func incrementLeftHand() {
        let newLeft = leftHand.count + 1
        if newLeft >= 5 {
            leftHand.setCount(0)
            rightHand.setCount(rightHand.count + 1)
        } else {
            leftHand.setCount(newLeft)
        }
    }
    
    // 右手加1(按需实现)
    func incrementRightHand() {
        rightHand.setCount(rightHand.count + 1)
    }
    
    // 重置所有计数
    func reset() {
        leftHand.setCount(0)
        rightHand.setCount(0)
    }
}

3. SwiftUI视图:纯展示+触发操作

视图只负责绑定状态、把用户操作转递给主模型,半毛钱逻辑都没有:

struct FingerCounterView: View {
    @StateObject private var model = FingerCounterModel()
    
    var body: some View {
        VStack(spacing: 30) {
            HandView(title: "左手", count: model.leftHand.count, onTap: model.incrementLeftHand)
            HandView(title: "右手", count: model.rightHand.count, onTap: model.incrementRightHand)
            
            Button("重置") {
                model.reset()
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
    }
}

// 完全哑的子视图,只做展示和事件传递
struct HandView: View {
    let title: String
    let count: Int
    let onTap: () -> Void
    
    var body: some View {
        VStack {
            Text(title)
                .font(.title)
            Text("\(count)")
                .font(.largeTitle)
                .fontWeight(.bold)
            Button("加1") {
                onTap()
            }
            .buttonStyle(.bordered)
        }
        .padding()
        .border(.gray, width: 2)
        .cornerRadius(8)
    }
}

为什么这方案能解决问题?

  • 彻底消除无限循环:没有监听子模型的objectWillChange再修改状态的逻辑,所有变更都是主模型主动触发,不会出现循环触发的死锁。
  • 效率拉满:砍掉了Combine订阅分发的额外开销,状态变更路径直接明了。
  • 严格符合哑组件设计:Hand模型和HandView完全无业务逻辑,所有规则都集中在主模型,后续改进位规则(比如改成满10进位),只需要改主模型的方法就行。

适配双视频同步场景的迁移思路

把这套逻辑直接套过去就行:

  • 把左右手换成两个视频的状态模型(比如VideoPlayerState,包含currentTime、isPlaying等属性,纯哑模型)
  • 主模型(比如SyncVideoModel)持有两个VideoPlayerState实例,对外提供同步拖动、同步播放的方法,在方法里处理两个视频的状态联动(比如拖动A视频进度时,同步修改B视频的进度)
  • 视频播放器视图只负责绑定状态、触发主模型的操作,完全不碰同步逻辑

内容的提问来源于stack exchange,提问作者chris P

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 21:55:11