UIKit中通过UIHostingController调用contactAccessPicker避免双重弹窗
在UIKit中直接展示iOS18的ContactAccessPicker(避免双重弹窗)
要解决双重弹窗问题,核心是让承载SwiftUI的UIHostingController完全不可见,仅触发系统原生的contactAccessPicker。以下是简洁的实现方案:
步骤1:创建SwiftUI包装视图
该视图无任何可见内容,仅用于触发和管理联系人选择器:
import SwiftUI struct ContactAccessPickerWrapper: View { @State private var isPickerPresented = true let selectionHandler: ([String]) -> Void let dismissHandler: () -> Void var body: some View { EmptyView() .contactAccessPicker(isPresented: $isPickerPresented) { identifiers in selectionHandler(identifiers) dismissHandler() } .onChange(of: isPickerPresented) { isActive in if !isActive { dismissHandler() } } } }
步骤2:在UIKit中调用展示
在你的UIKit视图控制器中,添加触发逻辑(例如绑定到导航栏按钮的点击事件):
import UIKit import SwiftUI class YourViewController: UIViewController { lazy var addContactButton: UIBarButtonItem = { let button = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(showContactPicker)) return button }() override func viewDidLoad() { super.viewDidLoad() navigationItem.rightBarButtonItem = addContactButton } @objc private func showContactPicker() { let pickerWrapper = ContactAccessPickerWrapper( selectionHandler: { selectedIdentifiers in // 处理选中的联系人标识符 print("选中的联系人ID:\(selectedIdentifiers)") self.fetchContacts(with: selectedIdentifiers) }, dismissHandler: { self.dismiss(animated: true) } ) let hostingController = UIHostingController(rootView: pickerWrapper) hostingController.modalPresentationStyle = .overFullScreen hostingController.view.backgroundColor = .clear present(hostingController, animated: false) } private func fetchContacts(with identifiers: [String]) { // 你的联系人获取逻辑 } }
方案说明
- 消除双重弹窗:
UIHostingController的视图为透明的EmptyView,用户只会看到系统原生的联系人选择器。 - 自动资源清理:无论用户选择联系人还是取消操作,都会触发回调销毁
UIHostingController,避免内存泄漏。 - 简化状态管理:利用SwiftUI的
@State自动触发选择器展示,无需额外的跨框架状态同步逻辑。
注意事项
- 必须将
modalPresentationStyle设置为.overFullScreen或.overCurrentContext,否则UIHostingController会替换当前视图栈导致异常。 - 弹出时关闭动画(
animated: false),避免不必要的视觉闪烁。
内容的提问来源于stack exchange,提问作者Z S
相关产品推荐
相关产品推荐

