ios - PresentationLink 始终使用相同的目标对象

标签 ios swift swiftui

令我惊讶的是,每当我点击 PresentationLink 中嵌入的按钮时,相同的 View 引用始终会出现。我的意思是我们不创建 View 的另一个实例。 这是有道理的,因为目标对象是在 body 属性中创建的,因此除非发生更改,否则不会重新创建。

你们知道我们是否有一种简单的方法可以在每次按下按钮时重新创建一个新 View 吗?还是设计使然,应该这样使用?

谢谢!

编辑

在@dfd 的评论之后,它似乎是设计好的。 现在如何处理这个用例:

Let's say I present a NavigationView and I pushed one view. If I dismiss and re present, I will go back on the view I previously pushed. In this case, I believe it's wrong as I'd like the user to go through the complete flow every single time. How can I make sure that I go back on the first screen everytime?

谢谢你(再次)!

编辑 2

这是一些代码:

struct PresenterExample : View {
    var body: some View {
        VStack {
            PresentationLink(destination: CandidateCreateProfileJobView()) {
                Text("Present")
            }
        }
    }
}

struct StackFirstView : View {
    var body: some View {
        NavigationLink(destination: StackSecondView()) {
            Text("Got to view 2")
        }
    }
}

struct StackSecondView : View {
    var body: some View {
        Text("View 2")
    }
}

在这种情况下,PresenterExample 呈现 StackFirstView,它将从 NavigationLink 推送 StackSecondView。 从那里,假设用户向下滑动并因此关闭了演示文稿。当它点击返回 PresenterExample 中的 PresentationLink 时,它将在 StackSecondView 上重新打开,这不是我想要的。我想再次显示 StackFirstView

更有意义? :)

最佳答案

第一次尝试:失败

我尝试使用 id告诉 SwiftUI 处理 StackFirstView 的每个演示文稿的修饰符作为与先前观点无关的全新观点:

import SwiftUI

struct PresenterExample : View {
    var body: some View {
        VStack {
            PresentationLink("Present", destination:
                StackFirstView()
                    .onDisappear {
                        print("onDisappear")
                        self.presentationCount += 1
                }
            )
        }
    }

    @State private var presentationCount = 0
}

struct StackFirstView : View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: StackSecondView()) {
                Text("Go to view 2")
            }.navigationBarTitle("StackFirstView")
        }
    }
}

struct StackSecondView : View {
    var body: some View {
        Text("View 2")
            .navigationBarTitle("StackSecondView")
    }
}

import PlaygroundSupport

PlaygroundPage.current.liveView = UIHostingController(rootView: PresenterExample())

应该执行以下操作:

  • 它应该识别 StackFirstView通过 presentationCount . SwiftUI 应该考虑每个 StackFirstView使用不同的标识符是完全不同的 View 。我已经成功地将其用于动画过渡。

  • 它应该增加 presentationCountStackFirstView被解雇,以便下一个StackFirstView获取不同的标识符。

问题是 SwiftUI 从不调用 onDisappear关闭当前 View 或其任何 subview 。我很确定这是一个 SwiftUI 错误(从 Xcode 11 beta 3 开始)。我提交了 FB6687752。

第二次尝试:失败成功

接下来,我尝试使用 presentation(Modal?) 自己管理演示文稿。修饰符,所以我不需要 onDisappear修饰符:

import SwiftUI

struct PresenterExample : View {
    var body: some View {
        VStack {
            Button("Present") {
                self.presentModal()
            }.presentation(modal)
        }
    }

    @State private var shouldPresent = false
    @State private var presentationCount = 0

    private func presentModal() {
        presentationCount += 1
        shouldPresent = true
    }

    private var modal: Modal? {
        guard shouldPresent else { return nil }
        return Modal(StackFirstView().id(presentationCount), onDismiss: { self.shouldPresent = false })
    }
}

struct StackFirstView : View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: StackSecondView()) {
                Text("Go to view 2")
            }.navigationBarTitle("StackFirstView")
        }
    }
}

struct StackSecondView : View {
    var body: some View {
        Text("View 2")
    }
}

import PlaygroundSupport

PlaygroundPage.current.liveView = UIHostingController(rootView: PresenterExample())

这以不同的方式失败了。 StackFirstView的第二次及以后的演讲只是呈现一个空白 View 。同样,我很确定这是一个 SwiftUI 错误。我提交了 FB6687804。

我尝试通过 presentationCount下降到 StackFirstView然后应用 .id(presentationCount) NavigationView 的修饰符的内容。如果模态在显示 StackSecondView 时被关闭并再次显示, Playground 就会崩溃.我提交了 FB6687850。

更新

This tweet from Ryan Ashcraft向我展示了一种解决方法,使第二次尝试成功。它包装了 Modal Group 中的内容, 并应用 id Group 的修饰符的内容:

import SwiftUI

struct PresenterExample : View {
    var body: some View {
        VStack {
            Button("Present") {
                self.presentModal()
            }.presentation(modal)
        }
    }

    @State private var shouldPresent = false
    @State private var presentationCount = 0

    private func presentModal() {
        presentationCount += 1
        shouldPresent = true
    }

    private var modal: Modal? {
        guard shouldPresent else { return nil }
        return Modal(Group { StackFirstView().id(presentationCount) }, onDismiss: { self.shouldPresent = false })
    }
}

struct StackFirstView : View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: StackSecondView()) {
                Text("Go to view 2")
            }.navigationBarTitle("StackFirstView")
        }
    }
}

struct StackSecondView : View {
    var body: some View {
        Text("View 2")
    }
}

import PlaygroundSupport

PlaygroundPage.current.liveView = UIHostingController(rootView: PresenterExample())

修改后的第二次尝试成功重置了 Modal 的状态在每个演示文稿上。请注意 id必须应用于 Group 的内容,而不是 Group本身,以解决 SwiftUI 错误。

第三次尝试:成功

我修改了第二次尝试,而不是使用 id修饰符,它包装了 StackFirstViewZStack 里面什么时候presentationCount是奇数。

import SwiftUI

struct PresenterExample : View {
    var body: some View {
        VStack {
            Button("Present") {
                self.presentModal()
            }.presentation(modal)
        }
    }

    @State private var shouldPresent = false
    @State private var presentationCount = 0

    private func presentModal() {
        presentationCount += 1
        shouldPresent = true
    }

    private var modal: Modal? {
        guard shouldPresent else { return nil }
        if presentationCount.isMultiple(of: 2) {
            return Modal(presentationContent, onDismiss: { self.shouldPresent = false })
        } else {
            return Modal(ZStack { presentationContent }, onDismiss: { self.shouldPresent = false })
        }
    }

    private var presentationContent: some View {
        StackFirstView()
    }
}

struct StackFirstView : View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: StackSecondView()) {
                Text("Go to view 2")
            }.navigationBarTitle("StackFirstView")
        }
    }
}

struct StackSecondView : View {
    var body: some View {
        Text("View 2")
    }
}

import PlaygroundSupport

PlaygroundPage.current.liveView = UIHostingController(rootView: PresenterExample())

这行得通。我想 SwiftUI 看到模态的内容每次都是不同的类型( StackFirstViewZStack<StackFirstView> )并且这足以说服它这些是不相关的 View ,因此它丢弃了先前呈现的 View 而不是重新使用它。

关于ios - PresentationLink 始终使用相同的目标对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57046623/

相关文章:

ios - Xcode 12 代码审查编辑器无法打开,因为它无法在源代码管理中找到文件

pattern-matching - 由于 Swift 模式匹配中的顺序导致的意外结果

ios - 在swiftui中按钮的触摸 Action 播放音频

iOS 在将图像保存到数组时保持 url 数组顺序

ios - 架构 i386 的 8 个重复符号

iOS - 将点击手势添加到 UITextField

ios - 如何创建新的 NSUrlSession(第一个 session 失效后)

swift - View 中的按钮在 View 容器中不起作用

ios - Text 或 Image 等 View 类型如何符合 SwiftUI 中的 View 协议(protocol)?

swiftui - 提取 subview 未显示