我有一个父视图,它有条件地显示子视图:
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")
}
但是,图像仍然会重新加载。
要么去掉创建_ConditionalView
的if
并使用修饰符,要么启用URLSession
的URLCache
,这样图像就会缓存在内存或磁盘中,而不会再次下载。
我唯一不好看的解决方法:
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()
}
}