SwiftUI:创建兼容iOS15的iOS16版contextMenu ViewModifier遇泛型问题
首先明确系统contextMenu方法里的泛型闭包含义:
() -> M:M是遵循ContextMenuMenuContent协议的类型,这个闭包用来生成菜单里的可操作选项(比如按钮、子菜单),所有能放进ContextMenu的内容都自动遵循该协议。() -> P:P是遵循ContextMenuPreviewContent协议的类型,这个闭包用来生成长按触发时弹出的预览视图,仅iOS16及以上支持,普通View都自动遵循该协议。
以下是适配iOS15的ViewModifier实现,通过泛型参数对齐系统API要求,结合版本分支完成兼容:
struct AdaptiveContextMenuModifier<M, P>: ViewModifier where M: ContextMenuMenuContent, P: ContextMenuPreviewContent { let menuContent: () -> M let previewContent: () -> P func body(content: Content) -> some View { if #available(iOS 16.0, *) { content.contextMenu(menuContent: menuContent, preview: previewContent) } else { content.contextMenu(ContextMenu(menuContent: menuContent)) } } } // 给View扩展便捷调用方法 extension View { func adaptiveContextMenu<M, P>(menuContent: @escaping () -> M, previewContent: @escaping () -> P) -> some View where M: ContextMenuMenuContent, P: ContextMenuPreviewContent { self.modifier(AdaptiveContextMenuModifier(menuContent: menuContent, previewContent: previewContent)) } }
代码说明
- 泛型约束:
AdaptiveContextMenuModifier的两个泛型参数M和P,完全对齐系统contextMenu方法的类型要求,保证类型安全。 - 版本兼容:iOS16+直接调用带预览的新版API,iOS15及以下回退到旧版
ContextMenu初始化方式,自动丢弃预览逻辑。 - 便捷调用:通过View扩展封装修饰器,调用方式和原生API一致,无需手动创建修饰器实例。
使用示例
Text("长按测试") .adaptiveContextMenu( menuContent: { Button("复制") { /* 执行复制逻辑 */ } Button("删除") { /* 执行删除逻辑 */ } }, previewContent: { Text("预览内容") .font(.title) .padding() .background(Color.gray.opacity(0.2)) .cornerRadius(8) } )
内容的提问来源于stack exchange,提问作者Z S
相关产品推荐
相关产品推荐

