如何在Mac Catalyst的UIKit应用中使用macOS专属SwiftUI视图?
解决方案:Mac Catalyst应用中集成macOS专属LocalAuthenticationView
一、解决SwiftUI视图类型不兼容问题
你碰到的Type 'any View' cannot conform to 'View'错误,核心原因是SwiftUI的View属于关联类型协议,any View作为存在类型无法直接被UIHostingController接收(该控制器要求传入具体的View实现类型)。正确的思路是在插件包中避免直接返回SwiftUI视图,转而返回包装好的视图控制器:
- 在macOS插件包中定义协议,返回
NSViewController:
// 插件包内的协议定义 public protocol LocalAuthProvider { func makeLocalAuthViewController() -> NSViewController } // 插件中的具体实现 public class MacLocalAuthProvider: LocalAuthProvider { public func makeLocalAuthViewController() -> NSViewController { let authView = LocalAuthenticationView() // 实例化macOS专属SwiftUI视图 return NSHostingController(rootView: authView) } }
- 主应用加载插件后,将
NSViewController转换为Catalyst兼容的UIViewController:
Catalyst支持将AppKit视图控制器的视图直接转为UIKit可用视图,也可以通过容器控制器管理生命周期:
// 主应用中加载插件并处理视图 if let provider = loadMacPlugin() as? LocalAuthProvider { let macVC = provider.makeLocalAuthViewController() // 将NSViewController的视图转为UIKit的UIView let authUIView = UIView(nsView: macVC.view) authUIView.frame = view.bounds view.addSubview(authUIView) // 若需要完整生命周期管理,添加为子控制器 addChild(macVC) macVC.didMove(toParent: self) }
二、直接使用AppKit的LAAuthenticationView
如果选择使用AppKit原生的LAAuthenticationView,完全可以在Mac Catalyst应用中加载并显示:
- 插件包中返回
LAAuthenticationView实例:
public protocol LocalAuthNSViewProvider { func makeLocalAuthNSView() -> LAAuthenticationView } public class MacLocalAuthNSViewProvider: LocalAuthNSViewProvider { public func makeLocalAuthNSView() -> LAAuthenticationView { let authView = LAAuthenticationView() // 配置视图属性,比如代理、样式等 authView.delegate = self return authView } }
- 主应用中转换为UIKit视图并嵌入:
if let provider = loadMacPlugin() as? LocalAuthNSViewProvider { let nsAuthView = provider.makeLocalAuthNSView() // 将NSView转为UIKit兼容的UIView let uiAuthView = UIView(nsView: nsAuthView) uiAuthView.frame = CGRect(x: 20, y: 20, width: 300, height: 100) view.addSubview(uiAuthView) // 视图代理逻辑可在插件内封装,或通过自定义协议向主应用传递事件 }
三、插件包配置关键注意事项
- 插件包必须设置为纯macOS目标,不能是Catalyst目标,确保能访问macOS专属API。
- 主应用需仅在Catalyst环境下加载插件,避免iOS环境报错:
func loadMacPlugin() -> Any? { #if targetEnvironment(macCatalyst) guard let pluginURL = Bundle.main.url(forResource: "MacAuthPlugin", withExtension: "bundle") else { return nil } guard let pluginBundle = Bundle(url: pluginURL) else { return nil } pluginBundle.load() return pluginBundle.classNamed("MacLocalAuthProvider")?.init() #else return nil #endif }
内容的提问来源于stack exchange,提问作者Igor Kulman
相关产品推荐
相关产品推荐

