首页 > 解决方案 > PresentationLink 始终使用相同的目标对象

问题描述

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

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

谢谢!

编辑

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

假设我展示了一个 NavigationView 并推送了一个视图。如果我解散并重新出现,我将返回之前推送的视图。在这种情况下,我认为这是错误的,因为我希望用户每次都完成完整的流程。如何确保每次都返回第一个屏幕?

再次感谢你)!

编辑 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")
    }
}

在这种情况下,这将是从 NavigationLink推送的PresenterExample礼物。从那里,假设用户向下滑动并因此关闭演示文稿。当它重新点击in时,它会重新打开,这不是我想要的。我想再次显示。StackFirstViewStackSecondViewPresentationLinkPresenterExampleStackSecondViewStackFirstView

更有意义?:)

标签: iosswiftswiftui

解决方案


第一次尝试:失败

我尝试使用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())

应该执行以下操作:

  • 它应该识别StackFirstViewby presentationCount。SwiftUI 应该将每个StackFirstView具有不同标识符的视图视为完全不同的视图。我已经成功地使用了动画过渡。

  • presentationCount它应该在关闭时递增StackFirstView,以便下一个StackFirstView获得不同的标识符。

问题是 SwiftUI 从不onDisappear为呈现的视图或其任何子视图调用闭包。我很确定这是一个 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只是呈现一个空白视图。同样,我很确定这是一个 SwiftUI 错误。我提交了 FB6687804。

我尝试将presentationCountdown 传递给StackFirstView然后将.id(presentationCount)修饰符应用于NavigationView's 的内容。如果模态在显示时被解除并再次呈现,这会使操场崩溃StackSecondView。我提交了 FB6687850。

更新

Ryan Ashcraft 的这条推文向我展示了一种解决方法,可以让第二次尝试生效。它将 ' 的内容包装Modal在 aGroup中,并将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修饰符,而是在StackFirstView内部包裹一个ZStackwhenpresentationCount是奇数。

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 每次都会看到模态的内容是不同的类型(StackFirstViewvs. ZStack<StackFirstView>),这足以说服它这些是不相关的视图,因此它会丢弃先前呈现的视图而不是重用它。


推荐阅读