UIPageViewController 中的 SwiftUI scrollViewDidScroll 无法正常工作

Posted

技术标签:

【中文标题】UIPageViewController 中的 SwiftUI scrollViewDidScroll 无法正常工作【英文标题】:SwiftUI scrollViewDidScroll in UIPageViewController not work properly 【发布时间】:2020-03-10 16:14:06 【问题描述】:

我按照Apple的关于interfacing-with-uikit的SwiftUI教程,我想得到UIScrollViewcontentOffset,所以我在PageViewController中定义了@Binding var progress: CGFloat,但在scrollViewDidScrollPageViewController中不会当我设置时正常工作 self.parent.progress = progress.

行为是当我滑动PageViewController时,它会自动返回。

当我将@Binding var progress: CGFloat 更改为@State var progress: CGFloat 时,PageViewController 将起作用,但我需要的是@Binding 而不是@State

这是完整的代码

import SwiftUI
import UIKit

struct ContentView: View 
    var body: some View 
        PageView([Color.red,Color.green,Color.yellow])
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    




struct PageViewController: UIViewControllerRepresentable 
    var controllers: [UIViewController]
    @Binding var currentPage: Int
    @Binding var progress : CGFloat

    func makeCoordinator() -> Coordinator 
        Coordinator(self)
    

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

        for view in pageViewController.view.subviews 
            if view is UIScrollView 
                (view as! UIScrollView).delegate = context.coordinator
            
        

        return pageViewController
    

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) 
        pageViewController.setViewControllers(
            [controllers[currentPage]], direction: .forward, animated: true)
    

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

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

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerBefore viewController: UIViewController) -> UIViewController?
        
            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 + 1 == parent.controllers.count 
                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
            
        

        func scrollViewDidScroll(_ scrollView: UIScrollView) 
            let width = scrollView.frame.size.width
            let offsetX = scrollView.contentOffset.x
            let progress = (offsetX - width) / width
            self.parent.progress = progress
        

    





struct PageView<Page: View>: View 
    var viewControllers: [UIHostingController<Page>]
    @State var currentPage = 0
    @State var progress : CGFloat = 0

    init(_ views: [Page]) 
        self.viewControllers = views.map  UIHostingController(rootView: $0) 
    

    var body: some View 
        VStack 
            PageViewController(controllers: viewControllers, currentPage: $currentPage,progress: $progress)
        
    


struct PageView_Previews: PreviewProvider 
    static var previews: some View 
        PageView([Color.red])
    


【问题讨论】:

【参考方案1】:

setViewControllers 移动到创建位置,如下所示

    ...
    pageViewController.setViewControllers( // << here !!
        [controllers[currentPage]], direction: .forward, animated: true)
    return pageViewController


func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) 

经过测试并适用于 Xcode 11.2 / ios 13.2

【讨论】:

以上是关于UIPageViewController 中的 SwiftUI scrollViewDidScroll 无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

UIPageViewController 中的 UIScrollView 滚动了多少?

如何删除 UIPageViewController 中的 previousViewControllers

UIPageViewController 中的奇怪约束行为

UIPageViewController 中的脊椎到底是啥?

如何更改 UIPageViewController 中的按钮名称

UIPageViewController 改变大小?