跳到主要内容

优化 SwiftUI 支持多平台代码

· 阅读需 4 分钟
GoSwiftUI
goswiftui.com

对于使用 SwiftUI 的多平台项目,它确实可以显著加快为多个平台开发的速度。然而,由于苹果的各个平台之间存在足够的差异,因此最终你的代码库将充斥着#if os()判断。

我以前曾写过关于在 SwiftUI 应用程序中共享跨平台代码的文章,其中我以UIPasteboardNSPasteboard之间的微小差异为例,以使应用程序在 iOS 和 macOS 上运行。但是,平台之间的 API 差异并不能解决您可能需要进行特定于平台的更改的所有情况。例如,UIKit 在 watchOS 和 tvOS 上(部分)可用。而 SwiftUI 在所有平台上都可用。

最好的平台特定差异示例之一是 UI 布局代码的填充值。David Smith 上周写了一篇关于他的一个手表应用程序的“像素完美”设计的文章。虽然他是针对每个手表设备尺寸调整 UI 布局而不是不同的平台,但核心问题是相同的:即使是自适应的 UI 布局代码也不能总是通用应用。

你最终得到的代码看起来像这样:

var body: some View {
MyCustomView()
#if os(iOS)
.padding(10)
#elseif os(watchOS)
.padding(4)
#else // macOS
.padding(24)
#endif
}

使用#if os()编写的代码很难阅读,增加了认知负荷,并且 Xcode 默认的#if os()格式非常丑陋。我们可以通过一个简单的扩展来避免在每个地方编写#if os(),同时使这段代码更易于阅读。

extension Double {
init(iOS: Self, watchOS: Self, macOS: Self) {
#if os(iOS)
self = iOS
#elseif os(watchOS)
self = watchOS
#else // macOS
self = macOS
#endif
}
}

然后我们的布局代码简化为以下形式:

var body: some View {
MyCustomView()
.padding(Double(iOS: 10, watchOS: 4, macOS: 24))
}

这样,我们立即清楚地知道我们总是想给这个视图一些填充,而值是特定于平台的。如果您喜欢,您还可以直接将此模式扩展到视图修饰符。如果我们对.padding()这样做,那么结果代码将进一步简化为:

var body: some View {
MyCustomView()
.padding(iOS: 10, watchOS: 4, macOS: 24)
}

我们显然不能在每个平台差异的场景中都使用这种模式,但它确实可以改善许多情况。我发现这是减少在特定视图的多个平台共享大部分 UI 代码时使用#if os()的好方法。但是,当必要时,您永远不应该犹豫地为特定平台构建完全独特的视图。在这些情况下,您可以将#if os()检查和平台差异提取到一个新的View中。

struct MyPlatformSpecificView: View {

var body: some View {
#if os(iOS)
self.body_iOS
#else // macOS
self.body_macOS
#endif
}

private var body_iOS: some View {
// the body for iOS only
}

private var body_macOS: some View {
// the body for macOS only
}
}

struct ContentView: View {

var body: some View {
MyPlatformSpecificView()
}
}

如果您正在编写大量的多平台 SwiftUI 代码,我希望这些小改进也能对您的代码优化有所帮助。