【问题标题】:Make view not redraw after updating ObservableObject更新 ObservableObject 后使视图不重绘
【发布时间】:2020-11-25 17:40:35
【问题描述】:

我有一种情况,我正在使用LazyVGrid 显示来自用户照片库的图像。网格有多个部分,每个部分包含要显示的图像数组,以及关于该部分的一些元数据。

我遇到的问题是,每次用户单击一个磁贴时,会在相应的图像中切换markedForDeletion网格中的所有视图(可见)都会重绘。这并不理想,因为在示例代码的“真实”版本中,重绘每个 Tile 会带来真正的惩罚,因为必须检索和渲染图像。

我尝试让Tile 符合Equatable,然后使用.equatable() 修饰符通知SwiftUI 不应重绘Tile,但这不起作用。

Section 之外放置另一个.onAppear 暗示每次发生任何变化时都会重绘整个部分,但我不确定如何构造我的代码,以便将重绘昂贵的Tiles 的影响降至最低。

import SwiftUI

struct ContentView: View {
    
    @ObservedObject var viewModel: ViewModel = ViewModel()
    
    let columns = [
        GridItem(.flexible(minimum: 40), spacing: 0),
        GridItem(.flexible(minimum: 40), spacing: 0),
    ]
    
    var body: some View {
        
        GeometryReader { gr in
            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(viewModel.imageSections.indices, id: \.self) { imageSection in
                        Section(header: Text("Section!")) {
                            ForEach(viewModel.imageSections[imageSection].images.indices, id: \.self) { imageIndex in
                                Tile(image: viewModel.imageSections[imageSection].images[imageIndex])
                                    .equatable()
                                    .onTapGesture {
                                        viewModel.imageSections[imageSection].images[imageIndex].markedForDeletion.toggle()
                                    }
                                    .overlay(Color.blue.opacity(viewModel.imageSections[imageSection].images[imageIndex].markedForDeletion ? 0.2 : 0))
                                
                                    .id(UUID())
                            }
                        }
                    }
                }
            }
        }
        
    }
}


struct Image {
    
    var id: String = UUID().uuidString
    var someData: String
    var markedForDeletion: Bool = false
    
}

struct ImageSection {
    
    var id: String = UUID().uuidString
    var images: [Image]
    
    
}

class ViewModel: ObservableObject {
    
    @Published var imageSections: [ImageSection] = generateFakeData(numSections: 10, numImages: 10)
    
}

func generateFakeData(numSections: Int, numImages: Int) -> [ImageSection] {
    
    var sectionsToReturn: [ImageSection] = []
    
    for _ in 0 ..< numSections {
        var imageArray: [Image] = []
        
        for i in 0 ..< numImages {
            imageArray.append(Image(someData: "Data \(i)"))
        }
        
        sectionsToReturn.append(ImageSection(images: imageArray))
    }
    
    return sectionsToReturn
}

struct Tile: View, Equatable {
    
    static func == (lhs: Tile, rhs: Tile) -> Bool {
        
        return lhs.image.id == rhs.image.id
    }
    
    
    var image: Image
    
    var body: some View {
        
        Text(image.someData)
            .background(RoundedRectangle(cornerRadius: 20).fill(Color.blue))
        
            .onAppear(perform: {
                NSLog("Appeared - \(image.id)")
            })
        
    }
    
}

【问题讨论】:

    标签: swift swiftui combine


    【解决方案1】:

    全局重绘的主要原因是.id(UUID()),因为每次调用它都会强制视图重新创建。但是要删除它并保持ForEach 可操作,我们需要明确地识别模型。

    这里是固定的代码部分。使用 Xcode 12.1 / iOS 14.1 测试

    1. 主循环
    ForEach(Array(viewModel.imageSections.enumerated()), id: \.1) { i, imageSection in
        Section(header: Text("Section!")) {
            ForEach(Array(imageSection.images.enumerated()), id: \.1) { j, image in
                Tile(image: image)
                    .onTapGesture {
                        viewModel.imageSections[i].images[j].markedForDeletion.toggle()
                    }
                    .overlay(Color.red.opacity(image.markedForDeletion ? 0.2 : 0))
            }
        }
    }
    
    1. 模型(注意 - 您的 Image 与标准 SwiftUI 冲突 Image - 避免此类冲突,否则您可能会遇到非常不清楚的错误。对于演示,我将它重命名为 ImageM
    struct ImageM: Identifiable, Hashable {
        
        var id: String = UUID().uuidString
        var someData: String
        var markedForDeletion: Bool = false
    }
    
    struct ImageSection: Identifiable, Hashable {
        
        var id: String = UUID().uuidString
        var images: [ImageM]
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-14
      • 2020-09-13
      • 2020-07-31
      • 2020-03-18
      • 1970-01-01
      相关资源
      最近更新 更多