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

相关推荐
Code&Ocean27 分钟前
iOS从Matter的设备认证证书中获取VID和PID
ios·matter·chip
/**书香门第*/28 分钟前
Laya ios接入goole广告,开始接入 2
ios
东坡肘子2 小时前
肘子的 Swift 周报 #063|异种肾脏移植取得突破
swiftui·swift·apple
恋猫de小郭16 小时前
什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap
flutter·ios·swiftui
网安墨雨20 小时前
iOS应用网络安全之HTTPS
web安全·ios·https
威化饼的一隅20 小时前
【多模态】swift-3框架使用
人工智能·深度学习·大模型·swift·多模态
福大大架构师每日一题21 小时前
37.1 prometheus管理接口源码讲解
ios·iphone·prometheus
BangRaJun2 天前
LNCollectionView-替换幂率流体
算法·ios·设计
刘小哈哈哈2 天前
iOS 多个输入框弹出键盘处理
macos·ios·cocoa
靴子学长2 天前
iOS + watchOS Tourism App(含源码可简单复现)
mysql·ios·swiftui