UIPageViewController:返回当前可见的视图

你怎么知道UIPageViewController显示的当前页面/视图是UIPageViewController

我已经重写了我的子视图的viewDidAppear方法,以便他们在viewDidAppear方法中向父视图发送一个id。

但是,问题是这样的:我不能可靠地使用该ID作为显示页面的ID。 因为如果用户翻页而中途决定停止翻页并放回页面,则viewDidAppear已经被调用。 (视图在curl页面后面可见)。

也许我应该只切换到一个新的ID如果当前视图消失。 但是,我想知道是否没有更简单的方法来返回当前可见的视图?

18 Solutions collect form web for “UIPageViewController:返回当前可见的视图”

您应该手动跟踪当前页面。 委托方法pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:会告诉你何时更新该variables。 方法transitionCompleted:的最后一个参数transitionCompleted:可以告诉你用户是否完成了翻页过程。

从iOS 6开始,我发现viewControllers属性不断更新,所以它始终保持代表当前页面的一个视图控制器,而没有别的。 因此,你可以通过调用viewControllers[0]来访问当前页面(假设你一次只显示一个视图控制器)。

一旦页面“locking”到位,viewController数组只会更新,所以如果用户决定部分显示下一个页面,除非他们完成了转换,否则它不会成为“当前”页面。

如果要跟踪“页码”,请在您通过UIPageViewController数据源方法创build索引值时为其分配索引值。


举个例子:

 -(void)autoAdvance { UIViewController *currentVC = self.viewControllers[0]; NSUInteger currentIndex = [myViewControllers indexOfObject:currentVC]; if ( currentIndex >= (myViewControllers.count-1) ) return; [self setViewControllers:@[myViewControllers[ currentIndex+1 ]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil]; } -(NSInteger)presentationIndexForPageViewController: (UIPageViewController *)pageViewController { // return 0; UIViewController *currentVC = self.viewControllers[0]; NSUInteger currentIndex = [myViewControllers indexOfObject:currentVC]; return currentIndex; } 

请注意 ,这是不可靠的评论

build立在奥列的答案…

这是我如何实现了4种方法来跟踪当前页面,并将页面指示符更新为正确的索引:

 - (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController{ return (NSInteger)[self.model count]; } - (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController{ return (NSInteger)self.currentIndex; } - (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers{ SJJeanViewController* controller = [pendingViewControllers firstObject]; self.nextIndex = [self indexOfViewController:controller]; } - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed{ if(completed){ self.currentIndex = self.nextIndex; } self.nextIndex = 0; } 

不幸的是,以上所有的方法都没有帮助我。 不过,我通过使用标签find了解决scheme。 也许这不是最好的,但它的工作原理,并希望它可以帮助别人:

 - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { if (completed) { int currentIndex = ((UIViewController *)self.pageViewController.viewControllers.firstObject).view.tag; self.pageControl.currentPage = currentIndex; } } 

在Swift中:(感谢@Jessy )

 func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { guard completed else { return } self.pageControl.currentPage = pageViewController.viewControllers!.first!.view.tag } 

例如: 要点

下面的解决scheme为我工作。

苹果可以通过使本地UIPageViewController滚动视图分页更加可configuration来避免很多麻烦。 我不得不求助于覆盖一个新的UIView和UIPageControl只是因为本地的UIPageViewController分页将不支持透明背景或在视图框架内重新定位。

 - (void)pageViewController:(UIPageViewController *)pvc didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { if (!completed) { return; } NSUInteger currentIndex = [[self.pageViewController.viewControllers lastObject] index]; self.pageControl.currentPage = currentIndex; } 

我通过使用一个小函数并指定pageIndex静态NSInteger跟踪页面索引。

 -(void) setPageIndex { DataViewController *theCurrentViewController = [self.pageViewController.viewControllers objectAtIndex:0]; pageIndex = [self.modelController indexOfViewController:theCurrentViewController]; } 

并调用[self setPageIndex]; 在由Ole指定的函数内部以及在检测到方向上的变化之后。

我第一次使用Corey的解决scheme,但它不是在iOS5上工作,然后结束了使用,

 - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed{ if(completed) { _currentViewController = [pageViewController.viewControllers lastObject]; } } 

它尝试通过不同的页面切换,现在运行良好。

Swift 3

没有不必要的代码 使用UIPageViewControllerDelegate方法。

 func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if finished { guard let index = (pageViewController.viewControllers?.first as! ContentViewController).index else { return } print(index) } } 

假设你的ContentViewController有索引属性,你应该已经使用了。

不幸的是上面没有为我工作。

我有两个视图控制器,当我稍微(约20px)向后滚动最后一个视图触发委托:

pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:

并说当前页面(索引)是0这是错误的。

在子ViewController里面使用委托类似于:

 - (void)ViewController:(id)VC didShowWithIndex:(long)page; // and a property @property (nonatomic) NSInteger index; 

那是在viewDidAppear里面触发的:

 - (void)viewDidAppear:(BOOL)animated { ... [self.delegate ViewController:self didShowWithIndex:self.index]; } 

为我工作。

这可靠地工作

我有一个自定义的UIPageController。 这个pageController.currentPage从viewWillAppear中显示的UIViewController更新

  var delegate: PageViewControllerUpdateCurrentPageNumberDelegate? init(delegate: PageViewControllerUpdateCurrentPageNumberDelegate ){ self.delegate = delegate super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewWillAppear(animated: Bool) { if delegate != nil { self.delegate!.upateCurrentPageNumber(0) //(0) is the pageNumber corresponding to the displayed controller } } //In the pageViewController protocol PageViewControllerUpdateCurrentPageNumberDelegate { func upateCurrentPageNumber(currentPageIndex: Int) } create the view display controllers initializing with the delegate orderedViewControllers = { return [ IntroductionFirstPageViewController(delegate: self), IntroductionSecondPageViewController(delegate: self), IntroductionThirdPageViewController(delegate: self) ] }() the function implementing the protocol func upateCurrentPageNumber(currentPageIndex: Int){ pageControl.currentPage = currentPageIndex } 

谢谢你的回答,我面临类似的问题,不得不存储索引。 我稍微修改我的代码,粘贴下面:

 - (MenuListViewController *)viewControllerAtIndex:(NSInteger)index { if (_menues.count < 1) return nil; // MenuListViewController *childViewController = [MenuListViewController initWithSecondSetFakeItems]; MenuListViewController *childViewController = self.menues[index]; childViewController.index = index; return childViewController; } #pragma mark - Page View Controller Data Source - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray<UIViewController *> *)previousViewControllers transitionCompleted:(BOOL)completed{ if (completed) { NSUInteger currentIndex = ((MenuListViewController *)self.pageController.viewControllers.firstObject).index; NSLog(@"index %lu", (unsigned long)currentIndex); } } - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { NSUInteger index = [(MenuListViewController *)viewController index]; if (index == 0) return nil; index --; return [self viewControllerAtIndex:index]; } - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { NSUInteger index = [(MenuListViewController *)viewController index]; index ++; if (index == _menues.count) return nil; return [self viewControllerAtIndex:index]; } 
 - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { NSLog(@"Current Page = %@", pageViewController.viewControllers); UIViewController *currentView = [pageViewController.viewControllers objectAtIndex:0]; if ([currentView isKindOfClass:[FirstPageViewController class]]) { NSLog(@"First View"); } else if([currentView isKindOfClass:[SecondPageViewController class]]) { NSLog(@"Second View"); } else if([currentView isKindOfClass:[ThirdViewController class]]) { NSLog(@"Third View"); } } //pageViewController.viewControllers always return current visible View ViewController 

下面的示例代码(在Swift 2中)演示了如何通过实现一个简单的图像Swiper教程。 代码本身的评论:

 import UIKit /* VCTutorialImagePage represents one page show inside the UIPageViewController. You should create this page in your interfacebuilder file: - create a new view controller - set its class to VCTutorialImagePage - sets its storyboard identifier to "VCTutorialImagePage" (needed for the loadView function) - put an imageView on it and set the contraints (I guess to top/bottom/left/right all to zero from the superview) - connect it to the "imageView" outlet */ class VCTutorialImagePage : UIViewController { //image to display, configure this in interface builder @IBOutlet weak var imageView: UIImageView! //index of this page var pageIndex : Int = 0 //loads a new view via the storyboard identifier static func loadView(pageIndex : Int, image : UIImage) -> VCTutorialImagePage { let storyboard = UIStoryboard(name: storyBoardHome, bundle: nil) let vc = storyboard.instantiateViewControllerWithIdentifier("VCTutorialImagePage") as! VCTutorialImagePage vc.imageView.image = image vc.pageIndex = pageIndex return vc } } /* VCTutorialImageSwiper takes an array of images (= its model) and displays a UIPageViewController where each page is a VCTutorialImagePage that displays an image. It lets you swipe throught the images and will do a round-robbin : when you swipe past the last image it will jump back to the first one (and the other way arround). In this process, it keeps track of the current displayed page index */ class VCTutorialImageSwiper: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate { //our model = images we are showing let tutorialImages : [UIImage] = [UIImage(named: "image1")!, UIImage(named: "image2")!,UIImage(named: "image3")!,UIImage(named: "image4")!] //page currently being viewed private var currentPageIndex : Int = 0 { didSet { currentPageIndex=cap(currentPageIndex) } } //next page index, temp var for keeping track of the current page private var nextPageIndex : Int = 0 //Mark: - life cylce override func viewDidLoad() { super.viewDidLoad() //setup page vc dataSource=self delegate=self setViewControllers([pageForindex(0)!], direction: .Forward, animated: false, completion: nil) } //Mark: - helper functions func cap(pageIndex : Int) -> Int{ if pageIndex > (tutorialImages.count - 1) { return 0 } if pageIndex < 0 { return (tutorialImages.count - 1) } return pageIndex } func carrouselJump() { currentPageIndex++ setViewControllers([self.pageForindex(currentPageIndex)!], direction: .Forward, animated: true, completion: nil) } func pageForindex(pageIndex : Int) -> UIViewController? { guard (pageIndex < tutorialImages.count) && (pageIndex>=0) else { return nil } return VCTutorialImagePage.loadView(pageIndex, image: tutorialImages[pageIndex]) } func indexForPage(vc : UIViewController) -> Int { guard let vc = vc as? VCTutorialImagePage else { preconditionFailure("VCPagImageSlidesTutorial page is not a VCTutorialImagePage") } return vc.pageIndex } //Mark: - UIPageView delegate/datasource func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? { return pageForindex(cap(indexForPage(viewController)+1)) } func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? { return pageForindex(cap(indexForPage(viewController)-1)) } func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [UIViewController]) { nextPageIndex = indexForPage(pendingViewControllers.first!) } func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if !finished { return } currentPageIndex = nextPageIndex } func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int { return tutorialImages.count } func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int { return currentPageIndex } } 

我一直在使用view.tag一会儿,试图跟踪当前页面太复杂了。

在这段代码中,索引存储在每个viewtag属性中,用于获取下一个或上一个VC。 使用这种方法也可以创build一个无限滚动。 查看代码中的注释以查看此解决scheme:

 extension MyPageViewController: UIPageViewControllerDataSource { func viewControllerWithIndex(var index: Int) -> UIViewController! { let myViewController = storyboard?.instantiateViewControllerWithIdentifier("MyViewController") as MyViewController if let endIndex = records?.endIndex { if index < 0 || index >= endIndex { return nil } // Instead, We can normalize the index to be cyclical to create infinite scrolling // if index < 0 { index += endIndex } // index %= endIndex } myViewController.view.tag = index myViewController.record = records?[index] return myViewController } func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? { let index = viewController.view?.tag ?? 0 return viewControllerWithIndex(index + 1) } func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? { let index = viewController.view?.tag ?? 0 return viewControllerWithIndex(index - 1) } func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int { return records?.count ?? 0 } func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int { return (pageViewController.viewControllers.first as? UIViewController)?.view.tag ?? 0 } } 

我有一个viewControllers数组,我显示在UIPageViewController

 extension MyViewController: UIPageViewControllerDataSource { func presentationCount(for pageViewController: UIPageViewController) -> Int { return self.viewControllers.count } func presentationIndex(for pageViewController: UIPageViewController) -> Int { return self.currentPageIndex } } extension MyViewController: UIPageViewControllerDelegate { func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if !completed { return } guard let viewController = previousViewControllers.last, let index = indexOf(viewController: viewController) else { return } self.currentPageIndex = index } fileprivate func indexOf(viewController: UIViewController) -> Int? { let index = self.viewControllers.index(of: viewController) return index } } 

这里需要注意的是, UIPageViewController的setViewControllers方法不会给出任何委托callback。 委托callback仅表示UIPageViewController中的用户触摸操作。

这是我提出的解决scheme:

 class DefaultUIPageViewControllerDelegate: NSObject, UIPageViewControllerDelegate { // MARK: Public values var didTransitionToViewControllerCallback: ((UIViewController) -> Void)? // MARK: Private values private var viewControllerToTransitionTo: UIViewController! // MARK: Methods func pageViewController( _ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController] ) { viewControllerToTransitionTo = pendingViewControllers.last! } func pageViewController( _ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool ) { didTransitionToViewControllerCallback?(viewControllerToTransitionTo) } } 

用法:

  let pageViewController = UIPageViewController() let delegate = DefaultUIPageViewControllerDelegate() delegate.didTransitionToViewControllerCallback = { pageViewController.title = $0.title } pageViewController.title = viewControllers.first?.title pageViewController.delegate = delegate 

确保设置了初始标题

处理这个恕我直言的最简单的方法是使用PageControl来存储转换的潜在结果,然后在转换被取消时恢复。 这意味着一旦用户开始滑动,页面控件就会改变,这对我来说是可以的。 这要求你有你自己的UIViewControllers数组(在这个例子中叫做allViewControllers

 func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) { if let index = self.allViewControllers.index(of: pendingViewControllers[0]) { self.pageControl.currentPage = index } } func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if !completed, let previousIndex = self.allViewControllers.index(of: previousViewControllers[0]) { self.pageControl.currentPage = previousIndex } } 
 UIViewController *viewController = [pageViewController.viewControllers objectAtIndex:0]; NSUInteger currentIndex = [(ViewController*) viewController indexNumber]; 

它将返回当前页面索引。 并且必须在UIPageViewController(didFinishAnimating)的委托函数下使用这个代码。