简体   繁体   English

SwiftUI:如何仅在 iOS 上有条件地呈现 ActionSheet?

[英]SwiftUI: how do I conditionally present ActionSheet only on iOS?

I've started to like an approach where I write crossplatform UI code with SwiftUI.我开始喜欢使用 SwiftUI 编写跨平台 UI 代码的方法。 The app would still be started with a native window/container, but have a fully crossplatform SwiftUI-driven UI.该应用程序仍将使用本机窗口/容器启动,但具有完全跨平台的 SwiftUI 驱动的 UI。 For many standard things things like list, navigationview etc, it is very useful and works fine.对于许多标准的东西,比如列表、导航视图等,它非常有用并且工作正常。

The problem arises with some platform-specific view extensions.某些特定于平台的视图扩展会出现问题。 I would like to write this code in a platform-agnostic fashion, but not sure how to do it for some specific cases.我想以与平台无关的方式编写此代码,但不确定如何在某些特定情况下执行此操作。

First, here's a working example of a crossplatform conditional view modifier.首先,这是一个跨平台条件视图修饰符的工作示例。

import SwiftUI

struct DemoView: View {
    var body: some View {
        Text("Hello, demo!").padding()
            .modifier(iosBackground())
    }
}

struct iosBackground: ViewModifier {
    #if os(OSX)
    func body(content: Content) -> some View {
        content
    }
    #else
    func body(content: Content) -> some View {
        content.background(Color.blue)
    }
    #endif
}

What the iosBackground modifier is doing, is applying a view modification only on the iOS platform (well, to be specific, on any non-OSX platform, but let's just work with OSX and iOS in this example). iosBackground修饰符所做的是仅在 iOS 平台上应用视图修改(好吧,具体来说,在任何非 OSX 平台上,但在此示例中我们只使用 OSX 和 iOS)。 The OSX version of the view is passed through, while the iOS version returns a modified view.视图的 OSX 版本通过,而 iOS 版本返回修改后的视图。 This color example is of course dumb and useless, but for layout-related things like padding, it is a highly practical approach.这个颜色的例子当然是愚蠢和无用的,但是对于布局相关的东西,比如填充,这是一个非常实用的方法。

My question: how do I apply the same approach to modifiers like actionSheet?我的问题:如何将相同的方法应用于像 actionSheet 这样的修饰符? Here's what I would like to do:这是我想做的事情:

struct DemoView: View {
    @State var showActionSheet = true
    var body: some View {
        Text("Hello, demo!").padding()
            .modifier(iosBackground())
            .actionSheet(isPresented: $showActionSheet) {
                ActionSheet(
                    title: Text("Actions"),
                    message: Text("Available actions"),
                    buttons: [
                        .cancel { },
                        .default(Text("Action")),
                        .destructive(Text("Delete"))
                    ]
                )
            }

    }
}

If you try to compile this code, it works fine on iOS.如果您尝试编译此代码,它在 iOS 上运行良好。 On OSX, it has a compilation error because the actionSheet API is not available on OSX.在 OSX 上,它有一个编译错误,因为actionSheet API 在 OSX 上不可用。 Which, indeed, is the case.确实如此。 I would like to make it so that the actionSheet call would simply be a no-op on OSX, but I can't figure out how to structure and conditionally compile my code to make it happen.我想这样做,以便actionSheet调用在 OSX 上只是一个空操作,但我无法弄清楚如何构造和有条件地编译我的代码以使其发生。

The question, once again: how can I structure this code so that on iOS, actionSheet would be presented, while on OSX, it would be a no-op?问题再次出现:我如何构造此代码,以便在 iOS 上显示 actionSheet,而在 OSX 上,它是空操作?

You are almost there.你快到了。 You would have found the way if you took a look at the .actionSheet 's function signature.如果您查看.actionSheet的函数签名,您就会找到方法。 It returns an opaque type of some View that is the return type of almost all the SwiftUI views.它返回some View的不透明类型,这是几乎所有 SwiftUI 视图的返回类型。 So, look at the documentation too:因此,也请查看文档:

/// Presents an action sheet.
///
/// - Parameters:
///     - isPresented: A `Binding` to whether the action sheet should be
///     shown.
///     - content: A closure returning the `ActionSheet` to present.
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)
@available(OSX, unavailable)
public func actionSheet(isPresented: Binding<Bool>, content: () -> ActionSheet) -> some View

That being said, you could use this as like as you have used the .background function in conjunction with the content.话虽如此,您可以像使用.background函数和内容一样使用它。 So, here is the solution:所以,这是解决方案:

struct Sheet: ViewModifier {
    @Binding var presented: Bool

    func body(content: Content) -> some View {
        #if os(OSX)
        return content
        #else
        return content
            .actionSheet(isPresented: $presented) {
                ActionSheet(title: Text("Action Title"),
                            message: Text("Action Message"),
                            buttons: [.cancel(), .default(Text("Ok"))]
                )
        }
        #endif
    }
}

I just moved the #if - #endif inside the function body and that requires the return keyword explicitly.我只是在函数体内移动了#if - #endif ,这需要明确的return关键字。 And you would use this as any modifier:您可以将其用作任何修饰符:

.modifier(Sheet(presented: $showActionSheet))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM