UIPageViewController可能返回错误的索引 [英] UIPageViewController can return the wrong index

查看:59
本文介绍了UIPageViewController可能返回错误的索引的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如标题所述,有一种方法可以返回错误的索引.这会弄乱页面底部的索引显示点.完成此操作的方法是跳过页面而不用手指离开屏幕.如果发生这种情况,则会弄乱其余的演示点.

As the title states, there is a way it can return the wrong index. This messes up the index presentation dots at the bottom of the page. The way this is done is by skipping a page without releasing a finger from the screen. If this happens, it messes of the rest of the presentation dots.

此处是实际运行中的错误.

here is what the bug it looks like in action.

这是用于UIPageViewController的代码.

And here is the code that was used for the 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
    }

}

任何帮助,将不胜感激,谢谢.

Any help with this would be greatly appreciated, thanks.

推荐答案

我提出了一种解决方法,因为它仍然没有解决!已经超过4年了.

I made a workaround because this is still not fixed!! It has been more than 4 years darn it.

从子类而不是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
    }
}

这篇关于UIPageViewController可能返回错误的索引的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆