提问者:小点点

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


我遵循了苹果的SwiftUI教程,在“与UIKIT接口”(https://developer.Apple.com/tutorials/SwiftUI/interfacing-with-uikit)下,我们被要求制作一个UIPageController。

在教程中,他们使用了一个状态属性来更新PageView中的CurrentPage变量。 我想尝试一下,决定将PageView嵌入到不同的视图中,并在父视图中更新CurrentPage属性。 因此,我很自然地将pageView中的@state属性更改为@binding属性,以便更新父级中的CurrentPage

但是,当我这样做时,UIPageController停止工作,并继续从PageViewController(_:ViewControllerBefore:)->返回nil; 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
            }
        }
    }
}

另外,从contentview中删除text修复了这个问题,但是我需要它--所以删除它不是一个选项。

如果有人能解释如何解决我的问题,我将万分感激。 如果你太懒,不需要提供一个代码示例,简单的解释也可以完成工作。

提前谢谢!


共1个答案

匿名用户

更新text时,在更新body期间重新创建pageView。 避免这种情况的解决方案是使pageview确认为equatable

fix最简单的演示如下(仅修改部分)。 使用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