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

SwiftUI中数组内对象拖拽时视图不更新问题求助

SwiftUI拖拽视图不更新:ObservableObject数组的观察问题

问题现象

编写的SwiftUI代码可正常编译并展示数组内容,但拖拽对象时,终端打印显示tOffset变量持续更新,视图却未同步刷新。怀疑是属性包装器使用不当,@ObservedObject对数组内的对象变化未正确响应。

核心原因

  1. 数组的Published通知机制:tokenArray中的@Published var tokens仅在数组本身的引用发生变化(如添加、删除元素)时才会触发视图更新。而数组内的varToken/expToken实例是引用类型,其内部@Published属性的变化不会触发数组的Published通知,因此父视图ViewDemo2无法感知到这些内部变化。
  2. 未跟踪子对象的变化:原视图中直接遍历tokens数组并使用其中的对象,但未对每个Token子类实例单独进行观察,导致这些实例内部的属性更新无法传递到视图刷新流程中。

解决方案

将每个Token对应的视图拆分为独立的子视图,并在子视图中使用@ObservedObject观察对应的Token实例,确保实例内部的@Published属性变化能触发视图刷新。

修改后的完整代码

类声明部分(保持不变)

class Token: Identifiable, ObservableObject{
    var id: UUID
    var name: String
    var scale: CGFloat
    
    @Published var sOffset: CGSize
    @Published var cOffset: CGSize
    @Published var tOffset: CGSize

    init(n: String) {
        id = UUID()
        name = n
        scale = 2.0

        sOffset = .zero
        cOffset = .zero
        tOffset = .zero
        
        updateTOffset()
    }
    
    public func updateTOffset() {
        tOffset.height = sOffset.height + cOffset.height
        tOffset.width = sOffset.width + cOffset.width
        
        print("Updated T Offset , x: \(tOffset.width), y:\(tOffset.height) ")
    }
}

class varToken: Token {
    var exp: expToken?
    
    init(name: String, exp: expToken?) {
        super.init(n: name)
        self.exp = exp
    }
}

class expToken: Token {
    @Published var bOffset: CGSize = .zero
    
    init(name: String) {
        super.init(n: name)
        sOffset.height = -10
        scale = 1.0
        updateTOffset()
    }
}

class tokenArray: Identifiable, ObservableObject {
    var id: UUID = UUID()
    @Published var tokens: [varToken] = [varToken(name: "1 +", exp: nil), varToken(name: "x", exp: expToken(name: "2"))]
}

视图部分(拆分后)

struct ViewDemo2: View {
    @ObservedObject var t: tokenArray = tokenArray()
    
    var body: some View {
        HStack(alignment: .bottom) {
            ForEach(t.tokens) { token in
                VarTokenView(token: token)
            }
        }
    }
}

// 封装varToken对应的视图
struct VarTokenView: View {
    @ObservedObject var token: varToken
    
    var body: some View {
        HStack(alignment: .bottom) {
            Text(token.name)
                .scaleEffect(token.scale, anchor: .center)
                .offset(token.tOffset)
                .padding(.leading, 10)
            
            // 处理expToken视图
            if let exp = token.exp {
                ExpTokenView(exp: exp)
            }
        }
    }
}

// 封装expToken对应的视图
struct ExpTokenView: View {
    @ObservedObject var exp: expToken
    
    var body: some View {
        Text(exp.name)
            .scaleEffect(exp.scale)
            .offset(exp.tOffset)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        withAnimation(.spring()) {
                            exp.cOffset = value.translation
                            exp.updateTOffset()
                        }
                    }
                    .onEnded { value in
                        withAnimation(.spring()) {
                            exp.cOffset = .zero
                            exp.updateTOffset()
                        }
                    }
            )
    }
}

说明

  • 拆分后的子视图通过@ObservedObject绑定对应的Token实例,当实例内部的@Published属性(如cOffset、tOffset)变化时,子视图会立即收到通知并刷新,解决了原视图无法感知内部变化的问题。
  • 这种拆分也符合SwiftUI的视图组合设计原则,让代码结构更清晰,职责更单一。

内容的提问来源于stack exchange,提问作者Simeon Shaffar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 13:25:38