一种比较好的 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新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
2501_916007471 小时前
手机使用过的痕迹能查到吗?完整查询指南与步骤
android·ios·智能手机·小程序·uni-app·iphone·webview
從南走到北2 小时前
JAVA国际版同城外卖跑腿团购到店跑腿多合一APP系统源码支持Android+IOS+H5
android·java·ios·微信小程序·小程序
alengan5 小时前
苹果企业签名流程
ios·iphone
Digitally13 小时前
如何在Mac上同步iPhone短信
macos·ios·iphone
2501_9151063216 小时前
App HTTPS 抓包 工程化排查与工具组合实战
网络协议·ios·小程序·https·uni-app·php·iphone
2501_9160088918 小时前
金融类 App 加密加固方法,多工具组合的工程化实践(金融级别/IPA 加固/无源码落地/Ipa Guard + 流水线)
android·ios·金融·小程序·uni-app·iphone·webview
2501_9159214318 小时前
Fastlane 结合 开心上架(Appuploader)命令行版本实现跨平台上传发布 iOS App 免 Mac 自动化上架实战全解析
android·macos·ios·小程序·uni-app·自动化·iphone
游戏开发爱好者820 小时前
iOS 上架要求全解析,App Store 审核标准、开发者准备事项与开心上架(Appuploader)跨平台免 Mac 实战指南
android·macos·ios·小程序·uni-app·iphone·webview
qixingchao20 小时前
iOS SwiftUI 动画开发指南
ios·swiftui·swift
alengan21 小时前
ios支付
macos·ios·cocoa