【问题标题】:SwiftUI: calling objectWillChange.send() not updating child viewSwiftUI:调用 objectWillChange.send() 不更新子视图
【发布时间】:2022-10-21 04:00:12
【问题描述】:

我有一组相当复杂的视图嵌套在视图中。当我触发一个按钮动作时,我通过我的viewModel 类传递一个可选块,该类在viewModel 上调用objectWillChange.send(),我知道它正在被触发,因为我的视图的其他部分正在更新。其中一个子视图(观察到viewModel 更改)直到我单击其中的一部分(更改viewModel.selectedIndex 并触发重绘,所以我知道它正在侦听已发布的更改)才会更新。

为什么更新不会触发子视图(在本例中为 PurchaseItemGrid)重绘自身?

这是我设置更新调用的地方...

struct RightSideView: View {
    @ObservedObject var viewModel: TrenchesPurchases
            
    var body: some View {
        VStack {
            ...
            PurchaseItemGrid(viewModel: viewModel)      // <-- View not updating
            Button {
                viewModel.purchaseAction() {
                    viewModel.objectWillChange.send()   // <-- Triggers redraw, reaches breakpoint here
                }
            } label: {
                ...
            }
            ...
        }
    }
}

这是调用可选项的地方(我不仅在视觉上确认这是在视图重绘的其他部分发生时,它还在此处达到断点)...

class TrenchesPurchases: ObservableObject, CanPushCurrency {

    // MARK: - Properties

    @Published private var model = Purchases()

    // MARK: - Properties: Computed

    var selectedIndex: Int {
        get { return model.selectedIndex }
        set { model.selectedIndex = newValue }
    }

    var purchaseAction: BlockWithBlock {
        { complete in                    
            ...
            
            complete?()
        }
    }
    ...
}

这是没有按预期更新的视图...

struct PurchaseItemGrid: View {

   @ObservedObject var viewModel: TrenchesPurchases
    
   var body: some View {
        VStack {
            itemRow(indices:  0...3)
            ...
        }
        ...
    }
    
    @ViewBuilder
    func itemRow(indices range: ClosedRange<Int>) -> some View {
        HStack {
            ForEach(viewModel.purchaseItems[range], id: \.id) { item in
                PurchaseItemView(item: item,
                                 borderColor: viewModel.selectedIndex == item.id ? .green : Color(Colors.oliveGreen))
                .onTapGesture { viewModel.selectedIndex = item.id }
            }
        }
    }
}

这是工作狗要求的代码...

struct Purchases {

    // MARK: - Properties

    var selectedIndex = 15

    let items: [PurchaseItem] = buildCollectionOfItems()

    // MARK: - Functions

    // MARK: - Functions: Static

    // TODO: Define Comments
    static func buildCollectionOfItems() -> [PurchaseItem] {
        return row0() + row1() + row2() + row3()
}

static func row0() -> [PurchaseItem] {
    var items = [PurchaseItem]()
    
    let grenade = Ammo(ammo: .grenade)
    items.append(grenade)
    
        let bullets = Ammo(ammo: .bullets)
        items.append(bullets)
    
        let infiniteBullets = Unlock(mode: .defense)
        items.append(infiniteBullets)
    
        let unlimitedInfantry = Unlock(mode: .offense)
        items.append(unlimitedInfantry)
    
        return items
    }

    static func row1() -> [PurchaseItem] {
        var items = [PurchaseItem]()
    
        for unit in UnitType.allCases {
            let item = Unit(unit: unit)
            items.append(item)
        }
        
        return items
    }

    static func row2() -> [PurchaseItem] {
        var items = [PurchaseItem]()

        let brits = NationItem(nation: .brits)
        items.append(brits)
    
        let turks = NationItem(nation: .turks)
        items.append(turks)
    
        let usa = NationItem(nation: .usa)
        items.append(usa)
    
        let insane = DifficultyItem(difficulty: .insane)
        items.append(insane)
    
        return items
    }

    static func row3() -> [PurchaseItem] {
        var items = [PurchaseItem]()

        let offenseLootBox = Random(mode: .offense)
        items.append(offenseLootBox)
    
        let defenseLootBox = Random(mode: .defense)
        items.append(defenseLootBox)
    
        let currency = Currency(isCheckin: false)
        items.append(currency)
    
        let checkIn = Currency(isCheckin: true)
        items.append(checkIn)
    
        return items
    }
}

【问题讨论】:

  • 您是否尝试过仅使用 viewModel.model.selectedIndex = item.id 而不是您的 var selectedIndex: Int {get..set..} 东西。你能告诉我Purchases 是什么吗?例如,一个结构体还是一个nested ObservedObject?
  • 那部分工作正常,它的购买动作块没有触发。 Purchases 是一个单独的结构(不是嵌套的)。
  • 如果如你所说,问题与purchaseAction 有关,至少向我们展示它的代码和结构Purchases,这样我们就可以评估发生了什么。
  • 我添加了Purchases 代码,但不确定这与发送发布通知的 viewModel 有什么关系。也许这就是我的问题。 purchaseAction 的代码一直存在,用 cmets 标记,上面写着“触发器重绘,在此处到达断点”。

标签: swift swiftui publisher property-wrapper


【解决方案1】:

我遇到的问题是PurchaseItemGrid 注意到正在发布的观察项目,但我试图触发的更改是在没有观察对象的PurchaseItemView 中。

我假设当PurchaseItemGrid 观察到变化并被重绘时,itemRow 方法将重绘PurchaseItemView 的新集合,然后更新它们的图像以匹配新状态。

这更加复杂,因为onTapGesture 触发了PurchaseItemView 的重绘,老实说,我仍然不确定PurchaseItemGrid 如何在仍然使用相同的PurchaseItemView 的同时重绘自身;但这可能与 @ViewBuilder 的工作方式有关,因为视图是在完全独立的方法中创建的。

所以,长话短说:确保您要更新的每个视图都有某种形式的观察者,不要依赖父级的重绘来创建新的子视图。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多