【问题标题】:UIPageViewController can return the wrong indexUIPageViewController 可以返回错误的索引
【发布时间】:2016-04-25 23:57:10
【问题描述】:

正如标题所述,有一种方法可以返回错误的索引。这会弄乱页面底部的索引表示点。完成此操作的方法是在不从屏幕上松开手指的情况下跳过一页。如果发生这种情况,它会弄乱其余的演示点。

here 是它在运行中的样子。

这是用于 UIPageViewController 的代码。

    import UIKit

class PageViewController: UIPageViewController {

    override func preferredStatusBarStyle() -> UIStatusBarStyle {
        return UIStatusBarStyle.LightContent
    }

    private(set) lazy var orderedViewControllers: [UIViewController] = {
        return [self.newColoredViewController("Green"),
            self.newColoredViewController("Red"),
            self.newColoredViewController("Blue")]

    }()

    private func newColoredViewController(color: String) -> UIViewController {
        return UIStoryboard(name: "Main", bundle: nil) .
            instantiateViewControllerWithIdentifier("\(color)ViewController")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        dataSource = self

        if let firstViewController = orderedViewControllers.first {
            setViewControllers([firstViewController],
                direction: .Forward,
                animated: true,
                completion: nil)
        }

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */

}

//MARK: UIPageViewControllerDataSource

extension PageViewController: UIPageViewControllerDataSource {

    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
            return nil
        }

        let previousIndex = viewControllerIndex - 1

        guard previousIndex >= 0 else {
            return nil
        }

        guard orderedViewControllers.count > previousIndex else {
            return nil
        }

        return orderedViewControllers[previousIndex]
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = orderedViewControllers.indexOf(viewController) else {
            return nil
        }

        let nextIndex = viewControllerIndex + 1

        let orderedViewControllersCount = orderedViewControllers.count

        guard orderedViewControllersCount != nextIndex else {
            return nil
        }

        guard orderedViewControllersCount > nextIndex else {
            return nil
        }

        return orderedViewControllers[nextIndex]
    }

    func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
        return orderedViewControllers.count
    }

    func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
        guard let firstViewController = viewControllers?.first,
            firstViewControllerIndex = orderedViewControllers.indexOf(firstViewController) else {
                return 0
        }

        return firstViewControllerIndex
    }

}

对此的任何帮助将不胜感激,谢谢。

【问题讨论】:

  • 根据您发布的视频,这听起来像是一个框架错误。我建议登录 Apple 错误报告器并在上面打开一个错误。
  • @DuncanC 我想是这样,我只是担心这可能只是代码错误。如果不是框架错误,而是我的代码中的缺陷,我不想提交错误报告。
  • 您如何设法用一根手指滑动并跳过页面?除非您的页面没有页面控制器的框架那么宽,否则这可能会使页面控制器感到困惑
  • @user3802077 我没有只用一根手指滑动。我让 PageController 认为我刷了一个。我小心地滑过一根手指,但没有取下它,然后我将另一根手指放在屏幕上,同时松开第一根手指。现在可以用第二根手指跳过整个页面。
  • 我也将记录一个错误。我可以很容易地重现这个问题。

标签: ios swift uipageviewcontroller


【解决方案1】:

我做了一个解决方法,因为这仍然没有解决!!已经4年多了。

类到子类而不是UIPageViewController

import UIKit

class PageController: UIPageViewController {
    lazy var pages: [UIViewController] = []

    var newOffset: CGFloat = 20

    var showReal = true
    var pageControlX: CGFloat = 0 // 0 is the middle of the screen
    var pageControlY: CGFloat = 200

    private var scrollView: UIScrollView?
    private var scrollPoints: [UIView] = []
    private var oldScrollPoints: [UIView] = []

    private var indexKeeper = IndexKeeper()

    private var selectedColor = UIColor(white: 1, alpha: 1)
    private var unselectedColor = UIColor(white: 1, alpha: 0.2)

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        // whole screen scroll
        scrollView = view.subviews.filter{ $0 is UIScrollView }.first! as? UIScrollView
        scrollView!.frame = UIScreen.main.bounds

        // get pageControl and scroll view from view's subviews
        let pageControl = view.subviews.filter{ $0 is UIPageControl }.first! as! UIPageControl
        oldScrollPoints = pageControl.subviews

        // remove all constraint from view that are tied to pageControl
        let const = view.constraints.filter { $0.firstItem as? NSObject == pageControl || $0.secondItem as? NSObject == pageControl }
        view.removeConstraints(const)

        // customize pageControl
        pageControl.translatesAutoresizingMaskIntoConstraints = false
        pageControl.frame = CGRect(x: pageControlX, y: pageControlY,
                                   width: view.frame.width, height: 0)

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
            self.replacePoints()
            self.updateIndex()
        }
    }

    private func replacePoints() {
        for point in scrollPoints {
            point.removeFromSuperview()
        }
        scrollPoints = []
        for oldPoint in oldScrollPoints {
            let newPoint = UIView(frame: oldPoint.frame)
            // make rounded
            newPoint.layer.borderWidth = 0
            newPoint.layer.masksToBounds = false
            newPoint.layer.cornerRadius = newPoint.frame.height / 2
            newPoint.clipsToBounds = true

            newPoint.center = CGPoint(x: oldPoint.center.x, y: newOffset)
            newPoint.backgroundColor = oldPoint.backgroundColor
            oldPoint.superview?.addSubview(newPoint)
            scrollPoints.append(newPoint)
            oldPoint.alpha = showReal ? 1 : 0
        }
    }

    private func offsetFromFirstPage() -> CGFloat {
        var coordinates: [CGFloat] = []
        for (index, page) in pages.enumerated() {
            let offset = scrollView!.convert(scrollView!.bounds.origin, to: page.view!)
            coordinates.append(offset.x + CGFloat(index) * view.frame.width)
        }

        let duplicates: [CGFloat] = coordinates.enumerated().map
        { current in
            if coordinates.firstIndex(of: current.element) != coordinates.lastIndex(of: current.element) {
                return current.element
            } else {
                return .nan
            }
        }

        return duplicates.first(where: { !$0.isNaN }) ?? 0
    }

    private var lastIndex: Int = 0
    private func pageIndexFrom(offset: CGFloat, visibleRatio: CGFloat=0.6) -> Int {
        let adjustedOffset = (offset / view.frame.width)

        if adjustedOffset > CGFloat(lastIndex) + visibleRatio {
            if lastIndex + 1 < pages.count {
                lastIndex += 1
            }
        } else if adjustedOffset < CGFloat(lastIndex) - visibleRatio {
            if lastIndex - 1 >= 0 {
                lastIndex -= 1
            }
        }
        return lastIndex
    }

    private func updateIndex() {
        let fastIndex = pages.firstIndex(of: viewControllers!.first!)!
        indexKeeper.fastIndexUpdate(index: fastIndex)

        let offset = offsetFromFirstPage()
        let slowIndex = pageIndexFrom(offset: offset)
        indexKeeper.slowIndexUpdate(index: slowIndex)

        for (index, point) in scrollPoints.enumerated() {
            if index == indexKeeper.finalIndex {
                point.backgroundColor = selectedColor
            } else {
                point.backgroundColor = unselectedColor
            }
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
            self.updateIndex()
        }
    }
}

class IndexKeeper {
    var finalIndex = 0
    private var slowIndex = 0
    private var fastIndex = 0

    func slowIndexUpdate(index: Int) {
        if index != slowIndex {
            slowIndex = index
            finalIndex = slowIndex
        }
    }

    func fastIndexUpdate(index: Int) {
        if index != fastIndex {
            fastIndex = index
            finalIndex = fastIndex
        }
    }
}

使用示例:

import UIKit

class PageViewController: PageController, UIPageViewControllerDataSource {

    private func pageInstance(name:String) -> UIViewController {
        return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        pages = [
            pageInstance(name: "FirstPage"),
            pageInstance(name: "SecondPage"),
            pageInstance(name: "ThirdPage"),
            pageInstance(name: "FourthPage")
        ]

        dataSource = self

        setViewControllers([pages.first!], direction: .forward, animated: false, completion: nil)
    }

    // get page before current page
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let index = pages.firstIndex(of: viewController), index > 0 else {
            return nil
        }

        return pages[index - 1]
    }

    // get page after current page
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let index = pages.firstIndex(of: viewController), index + 1 < pages.count else {
            return nil
        }

        return pages[index + 1]
    }

    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return pages.count
    }

    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        if let vc = viewControllers?.first {
            return pages.firstIndex(of: vc)!
        }
        return 0
    }
}

【讨论】:

  • 但是stackoverflow.com/q/12939280/341994 的任何修复都不能修复它吗?
  • 那些是关于以编程方式移动到不同的页面,然后页面的顺序不正确。 stackoverflow.com/a/12939384/6304086 在视频中展示了他们正在谈论的内容。
  • 无论如何这些修复都不起作用,因为它们所做的只是以不同的方式以编程方式导航。
猜你喜欢
  • 1970-01-01
  • 2011-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多