hoshy
hoshy

Reputation: 563

SwiftUI: NavigationLink's Destination initialized whenever its parent is redrawn

So I have a ParentView, which has a NavigationLink, leading to a UIViewControllerRepresentable-conforming PageViewController.

Now that ParentView also has some subscription on some publisher. Whenever that one is fired, not only will the ParentView redraw all its content (which it should), it will also re-initialize the (already presenting) PageViewController.

That leads to stuttering/glitching, because the PageViewController is already presenting and using the controllers that are continually being resetted.

Below is the ParentView and PageViewController (without the Coordinator stuff), both is pretty vanilla. The commented guard line is a hack I tried to prevent it from updating if displayed already. It helps but it's still stuttering on every swipe.

So the question is: How can we prevent the updating of a presented ViewController-wrapped-View when its presenting View is redrawn?

struct ParentView: View {

    @Binding var something: Bool

    var body: some View {
        NavigationLink(destination: PageViewController(controllers: controllers)) {
            Text("Push me")
        }
    }
}
final class PageViewController: UIViewControllerRepresentable {

    var controllers: [UIViewController]
    private var currentPage = 0

    init(controllers: [UIViewController]) {
        self.controllers = controllers
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)
        pageViewController.dataSource = context.coordinator
        pageViewController.delegate = context.coordinator

        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        // I tried this: guard pageViewController.viewControllers!.isEmpty else { return }
        pageViewController.setViewControllers(
            [controllers[currentPage]], direction: .forward, animated: true)
    }
}

Upvotes: 2

Views: 752

Answers (1)

A.Andino
A.Andino

Reputation: 128

If your controllers don't change after being displayed once, you can simply call:

pageViewController.setViewControllers([controllers[currentPage]], direction: .forward, animated: true)

from the makeUIViewController(context:) function instead of the updateUIViewController(:) function.

Upvotes: 1

Related Questions