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

前端 未结 4 1641
無奈伤痛
無奈伤痛 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<Content: View>(
            _ 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
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-12 13:00

    Thanks DoesData for giving me the direction.

    The solution was to use #is os(macOS) around the entire code and not only around the modifier itself.

    import SwiftUI
    
    struct ContentView: View {
    
      @State var selection: Int?
    
      var body: some View {
    
        #if os(macOS)
        HStack() {
          NavigationView {
            List () {
              NavigationLink(destination: FirstView(), tag: 0, selection: self.$selection) {
                Text("Click Me To Display The First View")
              } // End Navigation Link
    
              NavigationLink(destination: SecondView(), tag: 1, selection: self.$selection) {
                Text("Click Me To Display The Second View")
              } // End Navigation Link
    
            } // End list
              .frame(minWidth: 350, maxWidth: 350)
              .onAppear {
                self.selection = 0
            }
    
          } // End NavigationView
            .listStyle(SidebarListStyle())
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        } // End HStack
    
        #elseif os(iOS)
        HStack() {
          NavigationView {
            List () {
              NavigationLink(destination: FirstView(), tag: 0, selection: self.$selection) {
                Text("Click Me To Display The First View")
              } // End Navigation Link
    
              NavigationLink(destination: SecondView(), tag: 1, selection: self.$selection) {
                Text("Click Me To Display The Second View")
              } // End Navigation Link
    
            } // End list
              .frame(minWidth: 350, maxWidth: 350)
              .onAppear {
                self.selection = 0
            }
    
          } // End NavigationView
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        } // End HStack
        #endif
    
      } // End some View
    } // End ContentView
    
    struct ContentView_Previews: PreviewProvider {
      static var previews: some View {
        ContentView()
      }
    }
    
    0 讨论(0)
  • 2021-01-12 13:05

    WorkingDog, I try your elegante code with a very simple code to change the text color depending on the Target... but the text stays blue on both target and does not go red on MacOS!

    import SwiftUI
    
    struct ContentView: View {
    
        var body: some View {
    
          #if os(macOS)
          return monText.foregroundColor(Color.red)
          #elseif os(iOS)
           return monText.foregroundColor(Color.blue)
          #endif
          }
    
      var monText: some View {
        Text("Hello, World!")
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
    0 讨论(0)
  • 2021-01-12 13:06

    your better off doing this:

     import SwiftUI
    
     struct ContentView: View {
    
    @State var selection: Int?
    
    var body: some View {
        #if targetEnvironment(macCatalyst)
        return theList.listStyle(SidebarListStyle())
        #else
        return theList.navigationViewStyle(DefaultNavigationViewStyle())
        #endif
    }
    
     var theList: some View {
     HStack() {
       NavigationView {
         List () {
           NavigationLink(destination: FirstView(), tag: 0, selection: self.$selection) {
             Text("Click Me To Display The First View")
           } // End Navigation Link
    
           NavigationLink(destination: SecondView(), tag: 1, selection: self.$selection) {
             Text("Click Me To Display The Second View")
           } // End Navigation Link
    
         } // End list
         .frame(minWidth: 350, maxWidth: 350)
         .onAppear {
             self.selection = 0
         }
    
       } // End NavigationView
         .frame(maxWidth: .infinity, maxHeight: .infinity)
    
     } // End HStack
     } // End some View
     } // End ContentView
     }
    
    0 讨论(0)
提交回复
热议问题