SwiftUI复用UIViewRepresentable时丢失UIView引用问题求助
问题:SwiftUI中复用UIKit视图后原视图消失的解决办法
我尝试通过UIViewRepresentable在SwiftUI中复用UIKit视图。双击该可视图件后,希望进入全屏封面并复用同一UIView,但关闭全屏视图后,原UIView消失。尝试了多种方法未能解决,求指明方向。
图片展示:
- 初始加载:

- 全屏状态:

- 返回后状态:

原代码
struct ContentView: View { @State var isFullscreen: Bool = false /// Array of UIView objects var uiViewArray: [CustomUIView] = [CustomUIView(text: "1"), CustomUIView(text: "2"), CustomUIView(text: "3")] let cardWidth: CGFloat = 200 let cardHeight: CGFloat = 200 @StateObject var viewManager = ViewManager() var body: some View { return VStack { ScrollView { ForEach(uiViewArray, id: \.id) { uiView in LabelViewRepresentable(customUIView: uiView) .frame(width: cardWidth, height: cardHeight) .onTapGesture(count: 2) { viewManager.selectedFullscreenView = uiView isFullscreen.toggle() } } } } .background(Color.gray) .fullScreenCover(isPresented: $isFullscreen, content: { FullScreenView(isFullscreen: $isFullscreen, uiView: viewManager.selectedFullscreenView) }) } }
struct FullScreenView: View { @Binding var isFullscreen: Bool var uiView: CustomUIView? var body: some View { VStack { Button(action: { isFullscreen.toggle() }, label: { Text("Close") }) if uiView != nil { LabelViewRepresentable(customUIView: uiView!) } } } }
struct LabelViewRepresentable: UIViewRepresentable { var customUIView: CustomUIView init(customUIView: CustomUIView) { self.customUIView = customUIView } func makeUIView(context: Context) -> CustomUIView { customUIView.backgroundColor = .green return customUIView } func updateUIView(_ uiView: CustomUIView, context: Context) { } } class CustomUIView: UIView { var text: String var id = UUID().uuidString init(text: String) { self.text = text super.init(frame: .zero) let label = UILabel() label.frame = .init(origin: .zero, size: CGSize(width: 100, height: 100)) label.text = self.text self.addSubview(label) } required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } } class ViewManager: ObservableObject { var selectedFullscreenView: CustomUIView? }
问题根源
一个UIView实例只能同时存在于一个父视图层级中。当你把同一个CustomUIView实例同时传给列表里的LabelViewRepresentable和全屏的LabelViewRepresentable时,UIKit会自动将该视图从原父视图移除,添加到新的父视图中。关闭全屏后,原位置的视图已经不在原来的层级里,自然就消失了。
解决方法
方案一:复制UIView实例
每次进入全屏时,创建原UIView的副本,让两个视图实例各自独立,避免层级冲突。
- 修改
CustomUIView,添加复制初始化方法:
class CustomUIView: UIView { var text: String var id = UUID().uuidString init(text: String) { self.text = text super.init(frame: .zero) setupLabel() } // 新增复制初始化方法 init(copy view: CustomUIView) { self.text = view.text super.init(frame: .zero) setupLabel() } required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } // 抽离视图创建逻辑,避免重复代码 private func setupLabel() { let label = UILabel() label.frame = .init(origin: .zero, size: CGSize(width: 100, height: 100)) label.text = self.text self.addSubview(label) self.backgroundColor = .green } }
- 修改
ContentView中传递全屏视图的逻辑:
.onTapGesture(count: 2) { // 创建原视图的副本,而非直接传递原实例 viewManager.selectedFullscreenView = CustomUIView(copy: uiView) isFullscreen.toggle() }
方案二:数据驱动(推荐)
遵循SwiftUI设计理念,用数据描述视图状态,而非直接操作UIView实例,从根源避免层级冲突问题。
- 定义数据模型:
struct ViewModel: Identifiable { let id = UUID().uuidString let text: String }
- 修改
ContentView,用ViewModel数组替代UIView数组:
struct ContentView: View { @State var isFullscreen: Bool = false let viewModels: [ViewModel] = [ViewModel(text: "1"), ViewModel(text: "2"), ViewModel(text: "3")] let cardWidth: CGFloat = 200 let cardHeight: CGFloat = 200 @StateObject var viewManager = ViewManager() var body: some View { VStack { ScrollView { ForEach(viewModels) { model in LabelViewRepresentable(viewModel: model) .frame(width: cardWidth, height: cardHeight) .onTapGesture(count: 2) { viewManager.selectedModel = model isFullscreen.toggle() } } } } .background(Color.gray) .fullScreenCover(isPresented: $isFullscreen) { FullScreenView(isFullscreen: $isFullscreen, model: viewManager.selectedModel) } } }
- 更新
LabelViewRepresentable,根据ViewModel创建UIView:
struct LabelViewRepresentable: UIViewRepresentable { let viewModel: ViewModel func makeUIView(context: Context) -> CustomUIView { CustomUIView(text: viewModel.text) } func updateUIView(_ uiView: CustomUIView, context: Context) { // 如需更新视图,在此处处理状态变化 } }
- 修改
FullScreenView和ViewManager:
struct FullScreenView: View { @Binding var isFullscreen: Bool var model: ViewModel? var body: some View { VStack { Button(action: { isFullscreen.toggle() }, label: { Text("Close") }) if let model = model { LabelViewRepresentable(viewModel: model) } } } } class ViewManager: ObservableObject { var selectedModel: ViewModel? }
这种方式完全贴合SwiftUI的响应式设计,不仅解决了视图消失问题,还让代码更易于维护和扩展。
内容的提问来源于stack exchange,提问作者Sebastian Olivares
相关产品推荐
相关产品推荐

