首页 > 解决方案 > 如何删除已经渲染的不需要的视图而不是渲染需要的视图?

问题描述

我正在开发一个CustomForEach,它可以像 SwiftUI 中的普通 ForEach 一样工作和工作,这个CustomForEach有它自己的早期版本,它有一些问题可供我使用,这让我更多地了解 SwiftUI 并挑战我尝试解决问题,其中一个问题是找到一种方法来破坏渲染所有需要的视图的不需要的视图!

目前,当我更新lowerBound时,CustomForEach开始渲染新范围,这是可以理解的。但是新范围需要比以前更少的视图,对于已经渲染的视图再次渲染它们是不可理解的。

目标:我想找到一种方法来停止渲染所有需要的视图,因为它们已经存在并且不需要再次渲染,只需删除不需要的视图。而且我不想在CustomForEach中开始另一个昂贵的计算来找出视图是否已经存在!

struct TextView: View {
    let string: String

    var body: some View {            
        print("rendering " + string)
        return HStack {
            Text(string)
            Circle().fill(Color.red).frame(width: 5, height: 5, alignment: .center)
        }
    }
}

struct CustomForEachView<Content: View>: View {
    private let id: Int
    let range: ClosedRange<Int>
    let content: (Int) -> Content
    
    init(range: ClosedRange<Int>, @ViewBuilder content: @escaping (Int) -> Content) {
        self.id = range.lowerBound
        self.range = range
        self.content = content
    }
    
    // The issue is rendering all existed Views when lower Bound get updated, even we do not need to render new View in updating lower Bound!
    
    var body: some View {
        content(range.lowerBound)
        if let suffixRange = suffix(of: range) { 
            CustomForEachView(range: suffixRange, content: content)
        }
    }
    
    private func suffix(of range: ClosedRange<Int>) -> ClosedRange<Int>? {
        return (range.count > 1) ? (range.lowerBound + 1)...range.upperBound : nil
    }
}

struct ContentView: View {
    @State private var lowerBound: Int = -2
    @State private var upperBound: Int = 2
    
    var body: some View {
        HStack {
            CustomForEachView(range: lowerBound...upperBound) { item in
                TextView(string: item.description)                    
            }
        }
        
        HStack {
            Button("add lowerBound") { lowerBound += 1 }
            Spacer()
            Button("add upperBound") { upperBound += 1 }
        }
        .padding()
    }
}

标签: swiftswiftui

解决方案


首先,要理解的重要一点是SwiftUI.View结构不是在屏幕上呈现的视图实例。它只是对所需视图层次结构的描述。无论如何,这些实例都会被框架SwiftUI.View重新创建和拆除。

SwiftUI 框架负责实际的渲染。它可能会为此使用 UIViews,也可能不会。在大多数情况下,这是您无需担心的实现细节。


也就是说,您可以通过使用id修饰符向视图添加显式 ID 来帮助框架。这样 SwiftUI 可以使用它来跟踪哪个视图是哪个视图。

但是,我不确定这是否真的有帮助。只是一个想法。


推荐阅读