一种比较好的 SwiftUI API 兼容方案

这里每天分享一个 iOS 的新知识,快来关注我吧

前言

最近开始正式使用 SwiftUI 做项目了,但是发现了一个很大的问题,就是很多在 UIKit 中经常用的 API,在 SwiftUI 中需要很高的版本才能使用。

随便举几个例子,ScrollView 滚动关闭键盘的 keyboardDismissMode 属性,在 UIScrollView 中 iOS 7 就支持了,但在 SwiftUI 中 iOS 16 才支持。再比如,在 UIAlertController 上添加 TextField,在 UIKit 中 iOS 8 就支持,但在 SwiftUI 上,iOS 16 才支持。

所以现阶段不太可能用纯 SwiftUI 做项目,除非你的项目最低兼容 iOS 15,甚至是 16 以上。话说回来,在一些 API 低版本不支持的情况下,就需要用到一些兼容手段,最近看到了一些聪明的方式分享给大家。

错误的兼容方式

我们假设你的项目最低版本支持 iOS 14,在使用一些 14 上不兼容的 API 时会报错:

可以看到 cyan 这个颜色在 iOS 15 才能使用,有一种办法是增加在这个 VStack 下增加 #available 判断:

scss 复制代码
var body: some View {
    if #available(iOS 16.0, *) {
        VStack {
            Text("Hello, world!")
                .fontWeight(.bold)
                .background(Color.red)
                .foregroundColor(.blue)
                .padding()
                .frame(width: 100, height: 100)
            Text("Hello, world!")
                .fontWeight(.bold)
                .background(Color.red)
                .padding()
                .frame(width: 100, height: 100)
                .foregroundColor(.cyan)
        }
    } else {
        VStack {
            Text("Hello, world!")
                .fontWeight(.bold)
                .background(Color.red)
                .foregroundColor(.blue)
                .padding()
                .frame(width: 100, height: 100)
            Text("Hello, world!")
                .fontWeight(.bold)
                .background(Color.red)
                .padding()
                .frame(width: 100, height: 100)
                .foregroundColor(.blue)
        }
    }
}

这种方案有几个问题,首先为了使用 cyan,整个 VStack 都要被 if 语句包裹起来,如果这个 VStack 下代码非常多的话,会有很多的重复代码。其次这种方法不利于维护,如果再有一个属性是 iOS 16 才能用的,那么再用 if iOS 16 包裹一次吗?显然不合理。

正确的做法

正确的方法是通过扩展做兼容,我们可以给 Color 扩展出一个新的 newCyan 属性,来兼容这种情况:

swift 复制代码
extension Color {
    static let newCyan: Self = {
        if #available(iOS 15.0, *) {
            return .cyan
        } else {
            return .black
        }
    }()
}

这样的话,再使用的时候直接使用 newCyan 就行了:

scss 复制代码
Text("Hello, world!")
    .foregroundColor(.newCyan)

这样就解决了上边的问题。

其他的例子

使用扩展的兼容方案是个不错的思路,还有一些其他例子:

1、隐藏 List 的分割线 listRowSeparator 方法,在 iOS 15 才能用:

同样使用扩展:

swift 复制代码
func newHiddenListRowSeparator() -> some View {
    if #available(iOS 15.0, *) {
        return listRowSeparator(.hidden)
    } else {
        return self
    }
}

hiddenListRowSeparator 改成 newHiddenListRowSeparator 即可。

2、ScrollView 禁止滚动

swift 复制代码
func newScrollDisabled() -> some View {
    if #available(iOS 16.0, *) {
        return self.scrollDisabled(true)
    } else {
        return self
    }
}

3、View 的 tint 方法 iOS 15 以上才能用

那就给 View 写个扩展

swift 复制代码
extension View {
    func newTint(_ color: Color?) -> some View {
        if #available(iOS 16.0, *) {
            return self.tint(color)
        } else {
            return self.accentColor(color)
        }
    }
}

总结

使用扩展兼容 API 的方式好处显而易见:

  1. 解决了重复代码问题

  2. 每个 API 的兼容判断都是独立的,可维护性强

  3. 当你升级最低版本时,只需要把版本判断删掉或者直接把方法名改掉即可

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
开心就好20254 小时前
uni-app 本地打包 iOS,在 Windows 上使用 KXBuild 产出 ipa 并上架的完整流程
后端·ios
调试人生的显微镜4 小时前
跨平台抓包工具怎么选?TraceEagle抓包鹰 的四种跨平台能力
后端·ios
2501_915909066 小时前
Ipa Guard 防护上架,混淆、重签名和回归测试怎么做
android·ios·小程序·https·uni-app·iphone·webview
木头分享8 小时前
2026淘宝、京东双11活动全自动脚本丨IOS+安卓
ios
茶底世界之下8 小时前
红花太抢眼,蓝天又想留下:用 Awaking 比较两种减饱和
ios
2501_9160088910 小时前
Windows 上写第一个 SwiftUI App,KXBuild 空工程起步,跑到 iPhone 真机
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
2501_9159184111 小时前
iOS 上架注意事项有哪些?Appuploader 的四个阶段自查清单
android·ios·小程序·https·uni-app·iphone·webview
黑化旺仔1 天前
【OC】Runtime:类元数据、加载初始化与消息派发
开发语言·macos·ios·objective-c
songgeb1 天前
iOS App支持设置RTL语言的实践
ios·swift·ui kit
软泡芙1 天前
【iOS】swift启动屏显示黑屏
ios