UIPageViewController在SwiftUI中滑动1次后将停止工作

问题描述 投票:0回答:1

我遵循了SwiftUI的Apple教程,在“与UIKit交互”(https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit)下,我们制作了一个UIPageController。

在本教程中,他们使用State属性来更新currentPage中的PageView变量。我想尝试一下,然后决定将PageView嵌入到其他视图中,并在该父视图中进行currentPage属性更新。因此,自然地,我将@State中的@Binding属性更改为PageView属性,以便更新父级中的currentPage

但是,当我这样做时,UIPageController停止工作,并在第二次滑动时不断从nil返回pageViewController(_:viewControllerBefore:) -> UIViewController?

这是代码,我将添加注释以使它理解起来不那么麻烦:

struct ContentView: View {
    @State var currentPage = 0  // This is the new @State variable which should get updated.

    var body: some View {
        VStack {
            PageView([Text("First page"), Text("Second Page"), Text("Third Page")],
                      currentPage: self.$currentPage)  // Passed a binding to the State variable since I want that to be updated when the page changes.
            Text("Page number: \(currentPage)")  // Removing this fixes the problem, but I need it here.
        }
    }
}
struct PageView<Page: View>: View {
    var viewControllers: [UIHostingController<Page>]
    @Binding var currentPage: Int  // This used to be an @State property, but I changed it to a binding since the currentPage variable is now being updated elsewhere (In the view above).

    init(_ views: [Page], currentPage: Binding<Int>) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }  // Simply wrapping all the views inside ViewControllers for UIPageViewController to use.
        self._currentPage = currentPage  // Assigning the binding.
    }

    var body: some View {
        PageViewController(controllers: self.viewControllers, currentPage: self.$currentPage)
    }
}
struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]
    @Binding var currentPage: Int

    func makeCoordinator() -> Coordinator {  // Makes the coordinator, irrelevant to the problem
        Coordinator(self)
    }
    func makeUIViewController(context: Context) -> UIPageViewController { 
        // I don't think the problem lies here. 
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)

        pageViewController.dataSource = context.coordinator
        pageViewController.delegate = context.coordinator

        return pageViewController
    }
    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        // I don't think the problem lies here. 
        pageViewController.setViewControllers(
            [self.controllers[currentPage]], direction: .forward, animated: true)
    }

    class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
        var parent: PageViewController

        init(_ pageViewController: PageViewController) {
            self.parent = pageViewController
        }

        func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
            // This is where I think the problem lies. When the line below is evaluated, nil is returned on the second swipe when it shouldn't be. It works on the first swipe but on the second swipe nil is returned and therefore nothing is presented.
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index == 0 {
                return parent.controllers.last
            }
            return parent.controllers[index - 1]
        }
        func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index == parent.controllers.count - 1 {
                return parent.controllers.first
            }
            return parent.controllers[index + 1]
        }
        func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
            if completed, let visibleViewController = pageViewController.viewControllers?.first, let index = parent.controllers.firstIndex(of: visibleViewController) {
                parent.currentPage = index
            }
        }
    }
}

此外,Text中删除ContentView解决了这个问题,但我在那里需要它-因此,删除它不是一个选择。

[如果有人可以解释如何解决我的问题,我将非常感谢。 如果您太懒,则无需提供代码示例,简单的说明也可以完成工作。

提前感谢!

ios swift uikit swiftui uipageviewcontroller
1个回答
1
投票
最简单的修复程序演示如下(仅修改的部分)。经过Xcode 11.4 / iOS 13.4的测试]

注意:使用了相同的PageView,因为未提供PageView2

// ... var body: some View { VStack { PageView([Text("First page"), Text("Second Page"), Text("Third Page")], currentPage: self.$currentPage) .equatable() // << here !! Text("Page number: \(currentPage)") } } struct PageView<Page: View>: View, Equatable { // << confirm !! static func == (lhs: PageView<Page>, rhs: PageView<Page>) -> Bool { return true // just for demo, make it meaningful on some property } // .. other code goes here

© www.soinside.com 2019 - 2024. All rights reserved.