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

相关推荐
ssshooter38 分钟前
Tauri 2 iOS 开发避坑指南:文件保存、Dialog 和 Documents 目录的那些坑
前端·后端·ios
曾经我也有梦想12 小时前
ViewModifier 和 圆角以及渐变色
swiftui
harder32115 小时前
Swift 面向协议编程的 RMP 模式
开发语言·ios·mvc·swift·策略模式
leluckys16 小时前
Jenkins CI/CD 持续集成专题十二、iOS-Jenkins自动化打包集成
ios·ci/cd·jenkins
leluckys16 小时前
Jenkins CI/CD 持续集成专题八- jenkins 之 iOS 打包及上传至蒲公英
ios·ci/cd·jenkins
leluckys16 小时前
Jenkins CI/CD 持续集成专题十一、jenkins打包ios脚本证书
ios·ci/cd·jenkins
leluckys16 小时前
Jenkins CI/CD 持续集成专题十-jenkins 可以通过api 打ios包
ios·ci/cd·jenkins
壹方秘境16 小时前
作为开发者,我们需要的可能不是Wireshark那样的数据包分析工具,也不是Stream、ProxyPin那样的抓包工具
后端·ios
和沐阳学逆向1 天前
iOS逆向_古法逆向_Instagram最新版抓包
macos·ios·cocoa
Digitally1 天前
如何将真我(realme)手机数据传输至 iPhone
ios·智能手机·iphone