In SwiftUI, is it possible to use a modifier only for a certain os target?

前端 未结 4 1643
無奈伤痛
無奈伤痛 2021-01-12 12:16

Good day! In SwiftUI, is it possible to use a modifier only for a certain os target? In the following code I would like to use the modifier .listStyle(SidebarListStyle()) on

4条回答
  •  不思量自难忘°
    2021-01-12 12:47

    You can create a View extension and use it like this:

    List {
        // ...
    }
    .ifOS(.macOS) {
        $0.listStyle(SidebarListStyle())
    }
    

    Here's the implementation:

    enum OperatingSystem {
        case macOS
        case iOS
        case tvOS
        case watchOS
    
        #if os(macOS)
        static let current = macOS
        #elseif os(iOS)
        static let current = iOS
        #elseif os(tvOS)
        static let current = tvOS
        #elseif os(watchOS)
        static let current = watchOS
        #else
        #error("Unsupported platform")
        #endif
    }
    
    extension View {
        /**
        Conditionally apply modifiers depending on the target operating system.
    
        ```
        struct ContentView: View {
            var body: some View {
                Text("Unicorn")
                    .font(.system(size: 10))
                    .ifOS(.macOS, .tvOS) {
                        $0.font(.system(size: 20))
                    }
            }
        }
        ```
        */
        @ViewBuilder
        func ifOS(
            _ operatingSystems: OperatingSystem...,
            modifier: @escaping (Self) -> Content
        ) -> some View {
            if operatingSystems.contains(OperatingSystem.current) {
                modifier(self)
            } else {
                self
            }
        }
    }
    

    However, this will not work if you try to use a method that is not available for all the platforms you target. The only way to make that work is to use #if os(…) directly.

    I have an extension that makes it easier to do that:

    extension View {
        /// Returns a type-erased version of `self`.
        func eraseToAnyView() -> AnyView {
            AnyView(self)
        }
    }
    
    extension View {
        // The closure unfortunately has to return `AnyView` as `some` cannot yet be used in return values in closures.
        /**
        Modify the view in a closure. This can be useful when you need to conditionally apply a modifier that is unavailable on certain platforms.
    
        For example, imagine this code needing to run on macOS too where `View#actionSheet()` is not available:
    
        ```
        struct ContentView: View {
            var body: some View {
                Text("Unicorn")
                    .modify {
                        #if os(iOS)
                        return $0.actionSheet(…).eraseToAnyView()
                        #endif
    
                        return nil
                    }
            }
        }
        ```
        */
        @ViewBuilder
        func modify(_ modifier: (Self) -> AnyView?) -> some View {
            if let view = modifier(self) {
                view
            } else {
                self
            }
        }
    }
    

提交回复
热议问题