如何防止在SwiftUI中重新创建条件视图



我有一个父视图,它有条件地显示子视图:

struct HomeView: View {    
@State var selectedView = true
var body: some View {
VStack {
Picker("Selected", selection: $selectedView) {
Text("A").tag(true)
Text("B").tag(false)
}.pickerStyle(SegmentedPickerStyle())

if selectedView {
SubView()
} else {
Text("Other")
}
}
}
}

我的子视图包含一个从URL:加载的AsyncImage

struct SubView: View {
private let url = URL(string: "some url here")
var body: some View {
AsyncImage(url: url)
}
}

我的问题是,每次我将选择器切换到B,然后再切换回A时,都会重新创建SubView。这会重新创建AsyncImage,并导致从URL重新加载,这是不必要的。

有没有办法防止SubView在这里被重新创建?我注意到TabView在它们之间切换时似乎不会重新创建其包含的视图。使用我现有的结构是否可以获得此功能?

我尝试过使SubView相等,并在其实例上使用.equatable()修饰符,例如:

if selectedView {
SubView().equatable()
} else {
Text("Other")
}

但是,图像仍然会重新加载。

要么去掉创建_ConditionalViewif并使用修饰符,要么启用URLSessionURLCache,这样图像就会缓存在内存或磁盘中,而不会再次下载。

我唯一不好看的解决方法:

MyView().opacity(shouldHidden? 0 : 1)

因此,我发现之前使用不透明度的答案很糟糕,因为隐藏视图保留在视图层次结构中,我认为随着隐藏视图变得复杂,这可能是一个性能问题。因此,我提出了另一种方法,您可以将SwiftUI视图放入UIViewController中,并使用UIViewControllerRepresentable在SwiftUI代码中显示它。通过这种方式,您可以保存视图的状态,同时也可以将其从视图层次结构中删除。这里有一个最小的例子,你可以滚动滚动视图,然后单击按钮将其隐藏并再次显示,滚动视图保持在相同的偏移量,而不会被重新创建。希望能有所帮助。

import SwiftUI
let myView = MyView()
let hostingController = UIHostingController(rootView: myView)

struct MyView: View {
var body: some View {
ScrollView {
LazyVStack(alignment: .leading) {
ForEach(0 ... 100, id: .self) {i in
Text("(i)")
}
}
}

}
}
struct VC_Wrapper: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
return hostingController
}

func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
typealias UIViewControllerType = UIViewController
}
struct ContentView: View {
@State var isShow = true {
didSet {
hostingController.view.isHidden = !isShow
}
}
var body: some View {
ZStack {
VC_Wrapper()
Button("show/hide") {
isShow.toggle()
}.background(.red)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

最新更新