You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI复用UIViewRepresentable时丢失UIView引用问题求助

问题:SwiftUI中复用UIKit视图后原视图消失的解决办法

我尝试通过UIViewRepresentable在SwiftUI中复用UIKit视图。双击该可视图件后,希望进入全屏封面并复用同一UIView,但关闭全屏视图后,原UIView消失。尝试了多种方法未能解决,求指明方向。

图片展示:

  • 初始加载:InitialLoad
  • 全屏状态:Fullscreen
  • 返回后状态:ReturnFromFullscreen

原代码

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的副本,让两个视图实例各自独立,避免层级冲突。

  1. 修改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
    }
}
  1. 修改ContentView中传递全屏视图的逻辑:
.onTapGesture(count: 2) {
    // 创建原视图的副本,而非直接传递原实例
    viewManager.selectedFullscreenView = CustomUIView(copy: uiView)
    isFullscreen.toggle()
}

方案二:数据驱动(推荐)

遵循SwiftUI设计理念,用数据描述视图状态,而非直接操作UIView实例,从根源避免层级冲突问题。

  1. 定义数据模型:
struct ViewModel: Identifiable {
    let id = UUID().uuidString
    let text: String
}
  1. 修改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)
        }
    }
}
  1. 更新LabelViewRepresentable,根据ViewModel创建UIView:
struct LabelViewRepresentable: UIViewRepresentable {
    let viewModel: ViewModel
    
    func makeUIView(context: Context) -> CustomUIView {
        CustomUIView(text: viewModel.text)
    }

    func updateUIView(_ uiView: CustomUIView, context: Context) {
        // 如需更新视图,在此处处理状态变化
    }
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 22:05:25