您如何知道UIPageViewController
内显示的当前页面/视图是什么?
我已覆盖了我的子视图的viewDidAppear
方法,因此他们在viewDidAppear
方法中向父视图发送了一个ID。
然而,问题是:我无法可靠地将该id用作显示页面的id。因为如果用户转动页面但中途决定停止转动并将页面放回,则viewDidAppear
已经被调用。 (视图在卷曲页面后面可见)。
如果当前视图消失,我可能只会切换到新的ID。但我想知道是否有更简单的方法来返回当前可见的视图?
答案 0 :(得分:100)
您应该手动跟踪当前页面。委托方法pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:
将告诉您何时更新该变量。方法transitionCompleted:
的最后一个参数可以告诉您用户是否完成了翻页过渡。
答案 1 :(得分:57)
从iOS 6开始,我发现UIPageViewController的viewControllers
属性不断更新,因此它将始终保存一个代表当前页面的视图控制器,而不是其他内容。因此,您可以通过调用viewControllers[0]
来访问当前页面(假设您一次只显示一个视图控制器)。
一旦页面“锁定”到位,viewController数组才会更新,因此如果用户决定部分显示下一页,则除非他们完成转换,否则它不会成为“当前”页面。
如果要跟踪“页码”,请在通过UIPageViewController数据源方法创建视图控制器时为其分配索引值。
例如:
-(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;
}
但注意评论,这是不可靠的。
答案 2 :(得分:45)
不幸的是,以上所有方法都没有帮助我。不过,我通过使用标签找到了解决方案。可能它不是最好的,但它有效,并希望它可以帮助某人:
- (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
}
示例: gist
答案 3 :(得分:34)
以Ole的答案为基础......
这就是我实现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;
}
答案 4 :(得分:31)
以下解决方案对我有用。
通过使本机UIPageViewController滚动视图分页更加可配置,Apple可以避免许多麻烦。我不得不求助于覆盖新的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;
}
答案 5 :(得分:17)
Swift 4
没有不必要的代码。这样做的3种方法。使用 UIPageViewControllerDelegate 方法。
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard completed else { return }
// using content viewcontroller's index
guard let index = (pageViewController.viewControllers?.first as? ContentViewController)?.index else { return }
// using viewcontroller's view tag
guard let index = pageViewController.viewControllers?.first?.view.tag else { return }
// switch on viewcontroller
guard let vc = pageViewController.viewControllers?.first else { return }
let index: Int
switch vc {
case is FirstViewController:
index = 0
case is SecondViewController:
index = 1
default:
index = 2
}
}
答案 6 :(得分:11)
我通过使用一个小函数并将pageIndex指定为静态NSInteger来跟踪页面索引。
-(void) setPageIndex
{
DataViewController *theCurrentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
pageIndex = [self.modelController indexOfViewController:theCurrentViewController];
}
并在Ole指定的函数内调用[self setPageIndex];
,并在检测到方向更改后调用{{1}}。
答案 7 :(得分:5)
我第一次使用Corey的解决方案,但它不能在iOS5上运行,然后最终使用,
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed{
if(completed) {
_currentViewController = [pageViewController.viewControllers lastObject];
}
}
尝试切换不同的页面,现在效果很好。
答案 8 :(得分:3)
不幸的是,上面没有任何内容适用于我。
我有两个视图控制器,当我稍微(大约20px)向后滚动最后一个视图时,它会触发委托:
pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:
并说当前页面(索引)是0
这是错误的。
在子viewController中使用delegate,如:
- (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];
}
为我工作。
答案 9 :(得分:2)
这对我来说可靠
我有一个自定义的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
}
答案 10 :(得分:1)
我已经使用view.tag
一段时间了,试图跟踪当前页面太复杂了。
在此代码中,索引存储在每个tag
的{{1}}属性中,用于获取下一个或上一个VC。使用这种方法,也可以创建无限滚动。查看代码中的注释以查看此解决方案:
view
答案 11 :(得分:1)
感谢你的答案,我遇到类似的问题,不得不存储索引。我稍微修改了我的代码,将其粘贴到下面:
- (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];
}
答案 12 :(得分:0)
下面的演示代码(在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
}
}
答案 13 :(得分:0)
- (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
答案 14 :(得分: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 方法不会给出任何委托回调。委托回调仅代表 UIPageViewController 中的用户触摸操作。
答案 15 :(得分:0)
这是我提出的解决方案:
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
确保设置初始标题
答案 16 :(得分:0)
接近此IMHO的最简单方法是使用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
}
}
答案 17 :(得分:0)
在委托方法中跟踪我们当前的页码:
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController
我们使用助手getVC更新我们的页码并返回next / prev vc。这是一个好地方,因为只有在存在下一个或上一个vc时,才会调用getVC。
///Helper
func getVC(for pgNum: Int) -> BasePageVC {
let vc = storyboard?.instantiateViewController(withIdentifier: "page"+String(pgNum)) as! BasePageVC
vc.pageNumber = pgNum
self.currentPage = pgNum
vc.data = self.data
return vc
}
/// Next Page
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? BasePageVC else {
fatalError("Vc is not BasePageViewController in viewControllerAfter")
}
let nextPage = vc.pageNumber! + 1
guard nextPage < self.data.pages.count - 1 else { return nil }
return getVC(for: nextPage)
}
/// Previous Page
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let vc = viewController as? BasePageVC else {
fatalError("Vc is not BasePageViewController in viewControllerAfter")
}
let prevPage = vc.pageNumber! - 1
guard prevPage >= 0 else { return nil }
return getVC(for: prevPage)
}
答案 18 :(得分:0)
直接从viewController
(Swift 4版本)中请求UIPageViewController
怎么样:
fileprivate weak var currentlyPresentedVC: UIViewController?
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
currentlyPresentedVC = pageViewController.viewControllers?.first
}
或者,如果您仅在某个时间点需要当前显示的视图控制器,则只需在此时使用pageViewController.viewControllers?.first
。
答案 19 :(得分:0)
迅速做出5,并做出强的回答
extension InnerDetailViewController: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
guard let newIndex = embeddedViewControllers.firstIndex(where: { $0 == pageViewController.viewControllers?.last }) else { return }
print(newIndex)
currentEmbeddedViewControllerIndex = newIndex
}
}
}
在这种情况下,我不在乎嵌入了什么类的UIViewController
答案 20 :(得分:-1)
UIViewController *viewController = [pageViewController.viewControllers objectAtIndex:0];
NSUInteger currentIndex = [(ViewController*) viewController indexNumber];
它将返回当前页面索引。并且必须在UIPageViewController的委托函数下使用此代码(didFinishAnimating)。