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

相关推荐
与火星的孩子对话8 小时前
Unity进阶课程【六】Android、ios、Pad 终端设备打包局域网IP调试、USB调试、性能检测、控制台打印日志等、C#
android·unity·ios·c#·ip
恋猫de小郭1 天前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
点金石游戏出海2 天前
每周资讯 | Krafton斥资750亿日元收购日本动画公司ADK;《崩坏:星穹铁道》新版本首日登顶iOS畅销榜
游戏·ios·业界资讯·apple·崩坏星穹铁道
旷世奇才李先生2 天前
Swift 安装使用教程
开发语言·ios·swift
90后的晨仔2 天前
Xcode16报错: SDK does not contain 'libarclite' at the path '/Applicati
ios
finger244802 天前
谈一谈iOS线程管理
ios·objective-c
Digitally2 天前
如何将大型视频文件从 iPhone 传输到 PC
ios·iphone
梅名智2 天前
IOS 蓝牙连接
macos·ios·cocoa
美狐美颜sdk2 天前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭2 天前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin