SwiftUI:通用macOS和iOS视图的UserInterfaceSizeClass

问题描述

尝试在macOS(本机而非Catalyst)上引用@Environment对象horizontalSizeClassverticalSizeClass的对象会导致以下错误

'horizo​​ntalSizeClass'在macOS中不可用

'verticalSizeClass'在macOS中不可用

我很欣赏这些属性并不是真的适用于macOS,但这为创建通用的SwiftUI视图(即跨macOS,iOS等的跨平台)提供了很大的障碍。

一种解决方法是将所有特定于大小类的代码包装在条件编译中,但是结果是很多重复和冗余(请参见下面的示例)。

没有更有效的方法来处理此问题吗?

通用视图示例:

struct ExampleView: View {

    #if !os(macOS)
    @Environment(\.horizontalSizeClass) var horizontalSizeClass
    #endif

    private var item1: some View {
        Text("Example Item 1")
    }
    private var item2: some View {
        Text("Example Item 2")
    }
    private var item3: some View {
        Text("Example Item 3")
    }

    var body: some View {
        vstack {
            #if !os(macOS)
            if horizontalSizeClass == .compact {
                vstack {
                    item1
                    item2
                    item3
                }
            } else {
                HStack {
                    item1
                    item2
                    item3
                }
            }
            #else
            HStack {
                item1
                item2
                item3
            }
            #endif
        }
    }
}

解决方法

诚然,macOS本身并不支持horizontalSizeClassverticalSizeClass,但是好消息是添加它们很容易。

您可以通过创建符合@Environment的{​​{1}},然后使用其扩展struct来定义自己的EnvironmentKey对象。

在macOS上实现大小类所需要做的全部工作如下。他们始终会返回EnvironmentValues,但足以与iOS上的功能完全相同。

.regular

有了这个,您就不需要macOS的任何特殊功能。例如:

#if os(macOS)
enum UserInterfaceSizeClass {
    case compact
    case regular
}

struct HorizontalSizeClassEnvironmentKey: EnvironmentKey {
    static let defaultValue: UserInterfaceSizeClass = .regular
}
struct VerticalSizeClassEnvironmentKey: EnvironmentKey {
    static let defaultValue: UserInterfaceSizeClass = .regular
}

extension EnvironmentValues {
    var horizontalSizeClass: UserInterfaceSizeClass {
        get { return self[HorizontalSizeClassEnvironmentKey] }
        set { self[HorizontalSizeClassEnvironmentKey] = newValue }
    }
    var verticalSizeClass: UserInterfaceSizeClass {
        get { return self[VerticalSizeClassEnvironmentKey] }
        set { self[VerticalSizeClassEnvironmentKey] = newValue }
    }
}
#endif

只要将它们定义为struct ExampleView: View { @Environment(\.horizontalSizeClass) var horizontalSizeClass private var item1: some View { Text("Example Item 1") } private var item2: some View { Text("Example Item 2") } private var item3: some View { Text("Example Item 3") } var body: some View { VStack { if horizontalSizeClass == .compact { VStack { item1 item2 item3 } } else { HStack { item1 item2 item3 } } } } } ,您甚至可以将这些扩展放到更广泛使用的框架中。