UIPageViewController 可以返回错误的索引
Posted
技术标签:
【中文标题】UIPageViewController 可以返回错误的索引【英文标题】:UIPageViewController can return the wrong index 【发布时间】: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 认为我刷了一个。我小心地滑过一根手指,但没有取下它,然后我将另一根手指放在屏幕上,同时松开第一根手指。现在可以用第二根手指跳过整个页面。 我也将记录一个错误。我可以很容易地重现这个问题。 【参考方案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
【讨论】:
但是***.com/q/12939280/341994 的任何修复都不能修复它吗? 那些是关于以编程方式移动到不同的页面,然后页面的顺序不正确。 ***.com/a/12939384/6304086 在视频中展示了他们正在谈论的内容。 无论如何这些修复都不起作用,因为它们所做的只是以不同的方式以编程方式导航。以上是关于UIPageViewController 可以返回错误的索引的主要内容,如果未能解决你的问题,请参考以下文章
使用 UIPageViewController 的第一页时向左滑动返回
UIPageViewController viewControllerAfterViewController 一旦返回 nil 就不会被调用
如何从 UIPageViewController 中的视图控制器返回上一个 VC 或下一个 VC?