如何在iOS 15及早期版本上支持程序化视图解除。

15

在 iOS 14 及更早版本中,我们导入 presentationMode 环境变量:

@Environment(\.presentationMode) var presentationMode

然后调用self.presentationMode.wrappedValue.dismiss()来关闭视图。

在iOS 15上,此方法已被弃用,并引入了一个新的环境变量dismiss

@Environment(\.dismiss) var dismiss

我们可以直接使用dismiss()调用相应的函数以达到同样的效果。

我知道我们可以执行以下操作来调用适当的 dismiss 函数并支持所有版本:

if #available(iOS 15, *) {
    self.dismiss()
} else {
    self.presentationMode.wrappedValue.dismiss()
}

但是我该如何导入/定义正确的环境变量呢?尝试这样做并不起作用:

但是我该如何导入/定义正确的环境变量呢?尝试这样做并不起作用:

if #available(iOS 15, *) {
    @Environment(\.dismiss) var dismiss
} else {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
}

编辑:

显然,在 iOS 15 Beta 4 上,仍可以使用 presentationMode 在导航堆栈中解除视图。除非在 NavigationView 中存在 TabView:

struct ContentView: View {
    var body: some View {
        NavigationView {
            TabView {
                NavigationLink(destination: ChildView()) {
                    Text("View Child")
                }
            }
        }
    }
}

struct ChildView: View {
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        Button(action: {
            print("Popping...")
            self.presentationMode.wrappedValue.dismiss()
        }, label: {
            Text("POP")
                .font(.headline)
        })
    }
}

presentationMode 在这种情况下无法工作。


4
我建议您要么只针对iOS 15并使用新的方法,要么如果您支持较旧的版本,请继续使用弃用的方法。它仍然可以正常工作。 - Paulw11
根据您的问题,我看到您的代码中将会有一个@Environment!为了忽略View,为什么不直接使用和处理原始值?并且忘记dismisspresentationMode - ios coder
1
@Paulw11 抱歉,它确实可以工作。我在 nav 视图中有一个 TabView,在这种情况下,它不起作用。 - mota
1
@mota:仅使用dismiss而不提及真正的绑定似乎很酷!但这会给SwiftUI或您的项目带来很多工作,以找出您所提到的内容。 - ios coder
我刚刚尝试了你的代码,它在最新的Xcode(版本13.0(13A233))上运行良好,目标是iOS 14及以上。 - vicegax
显示剩余5条评论
4个回答

9

为了支持dismiss和 iOS 14,请将此扩展添加到您的EnvironmentValues中。

@available(iOS 14.0, *)
extension EnvironmentValues {
    var dismiss: () -> Void {
        { presentationMode.wrappedValue.dismiss() }
    }
}

2

我知道这个苹果公司还没有修复SwiftUI中的bug。到目前为止,我正在遵循以下方法,它对我所有版本都有效。也许对您的情况有帮助。

//presentationMode.wrappedValue.dismiss()
   
    if var topController = UIApplication.shared.windows.first!.rootViewController {
        while let presentedViewController = topController.presentedViewController {
            topController = presentedViewController
        }
        topController.dismiss(animated: true)
    }

1
谢谢,但我不确定在SwiftUI中如何使用它。你能再解释一下吗? - mota
谢谢!我在我的弹出窗口中有一个滚动视图,在iOS 16中@Environment(.dismiss)不起作用,@Binding var showPopover = false也不起作用。这样可以完美地关闭我的弹出窗口。 - candyline

2

由于我不喜欢“只使用已弃用的东西因为它能工作”的方法,我也在寻找这个问题的答案。

到目前为止,我能想到的最好的解决方案是,既不需要太多代码更改,又在可用时使用DismissAction

extension EnvironmentValues {
    // this can be used as: @Environment(\.dismissable) var myDismiss
    // in any swiftui view and it will not complain about ios versions 
    var dismissable: () -> Void {
        return dismissAction
    }
    

    // this function abstracts the availability check so you can
    // avoid the conflicting return types and any other headache
    private func dismissAction() {
        if #available(iOS 15, *) {
            dismiss()
        } else {
            presentationMode.wrappedValue.dismiss()
        }
    }
}

以防万一,如果有人对这个新环境变量的使用不清楚,在此提供一个示例(省略了一些部分,比如导航或呈现):

// view inside navigation/sheet
struct SomeInnerView: View {
    @Environment(\.dismissable) var myDismiss
    
    var body: some View {
        VStack {
            Button("dismiss me way 1") { myDismiss() } // tapping will result in a dismissing of this view
            Button("dismiss me way 2", action: myDismiss) // p.s this was not directly possible with DismissAction
        }
    }
}

0

你可以使用这个,对我很有帮助;

@Environment(.dismiss) var dismiss

这是源代码,在这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接