#available的条件修饰符无法正确执行

时间:2020-11-09 12:11:04

标签: swiftui

我有一个自定义修饰符,可以用图像视图替换导航栏标题,在iOS 14中,使用.toolbar修饰符非常简单,但是在iOS 13中,它需要做更多的工作,但是有可能。

当我想在条件修饰符中同时使用两个解决方案时,问题就来了,以下代码重现了该问题,它在iOS 14上运行时可以运行,但在iOS 13上却没有任何结果,但是如果“ #available”条件是从修改器中删除,仅保留iOS 13代码,它可以正常工作。将iOS 14包装在AnyView中也无济于事:

    extension View {
        @ViewBuilder
        func configuresIcon() -> some View {
            if #available(iOS 14.0, *){
                self.modifier(NavigationConfigurationView14Modifier())
            } else {
                self.modifier(NavigationConfigurationViewModifier(configure: { nv in
                    nv.topItem?.titleView = UIImageView(image: UIImage(named: IMAGE_NAME_HERE))
                }))
            }
        }
    }
    
    
    struct NavigationConfigurationViewModifier: ViewModifier {
        let configure: (UINavigationBar) -> ()
        
        func body(content: Content) -> some View {
            content.background(NavigationControllerLayout(configure: {
                configure($0.navigationBar)
            }))
        }
    }
    
    @available(iOS 14.0, *)
    struct NavigationConfigurationView14Modifier: ViewModifier {
        func body(content: Content) -> some View {
            content
                .toolbar {
                    ToolbarItem(placement: .principal) {
                        Image(IMAGE_NAME_HERE)
                    }
                }
        }
    }

    struct NavigationControllerLayout: UIViewControllerRepresentable {
        var configure: (UINavigationController) -> () = { _ in }
        
        func makeUIViewController(
            context: UIViewControllerRepresentableContext<NavigationControllerLayout>
        ) -> UIViewController {
            UIViewController()
        }
        
        func updateUIViewController(
            _ uiViewController: UIViewController,
            context: UIViewControllerRepresentableContext<NavigationControllerLayout>
        ) {
            if let navigationContoller = uiViewController.navigationController {
                configure(navigationContoller)
            }
        }
    }

2 个答案:

答案 0 :(得分:0)

尝试将内容包装在Group中(未经测试-唯一的想法)

extension View {
    @ViewBuilder
    func configuresIcon() -> some View {
      Group {
        if #available(iOS 14.0, *){
            self.modifier(NavigationConfigurationView14Modifier())
        } else {
            self.modifier(NavigationConfigurationViewModifier(configure: { nv in
                nv.topItem?.titleView = UIImageView(image: UIImage(named: IMAGE_NAME_HERE))
            }))
        }
     }
   }
}

答案 1 :(得分:0)

如果我们在运行时而不是在编译时检查OS版本,也许会起作用(因为buildlimitedavailability(_:)本身就是available only since iOS 14)。

 extension View {
    func erase() -> AnyView {
        return AnyView(self)
    }
    func applyIf<VM1: ViewModifier, VM2: ViewModifier>(_ condition: @autoclosure () -> Bool, ApplyIfTrue: VM1, ApplyIfFalse: VM2
) -> AnyView {
         if condition() {
            return self.modifier(ApplyIfTrue).erase()
         } else {
            return self.modifier(ApplyIfFalse).erase()
         }
     }
    
    @ViewBuilder func configuresIcon() -> some View {
        self.applyIf(NSProcessInfo().isOperatingSystemAtLeastVersion(NSOperatingSystemVersion(majorVersion: 14, minorVersion: 0, patchVersion: 0)),
                     ApplyIfTrue: NavigationConfigurationView14Modifier(),
                     ApplyIfFalse: modifier(NavigationConfigurationViewModifier(configure: { nv in
                        nv.topItem?.titleView = UIImageView(image: UIImage(named: IMAGE_NAME_HERE))
                     })))
       }
 }

如果您确实需要#available选项,可以尝试以下几种方法。 1)可能是因为扩展点不合适,所以请尝试将其移出View的扩展名。 2)将逻辑从修饰符移到主体。

例如下面的代码可以正常工作。

@available(macOS 10.15, iOS 13.0, *)
struct ContentView: View {
    var body: some View {
        ScrollView {
            if #available(macOS 11.0, iOS 14.0, *) {
                LazyVStack {
                    ForEach(1...1000, id: \.self) { value in
                        Text("Row \(value)")
                    }
                }
            } else {
                VStack {
                    ForEach(1...1000, id: \.self) { value in
                        Text("Row \(value)")
                    }
                }
            }
        }
    }
}