Как узнать, какая текущая страница / представление отображается внутри UIPageViewController
?
Я переопределил viewDidAppear
метод моих дочерних представлений, чтобы они отправляли идентификатор родительскому представлению в своем viewDidAppear
методе.
Однако проблема заключается в следующем: я не могу надежно использовать этот идентификатор в качестве идентификатора для отображаемой страницы. потому что, если пользователь переворачивает страницу, но на полпути решает остановить поворот и вернуть страницу, viewDidAppear
она уже будет вызвана. (вид виден за загнутой страницей).
Возможно, мне следует переключиться на новый идентификатор только в том случае, если текущее представление исчезнет. Но мне интересно, нет ли более простого способа вернуть вид, который сейчас виден?
viewDidAppear:animated:
вместо этого?Ответы:
Вы должны вручную отслеживать текущую страницу. Метод делегата
pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:
сообщит вам, когда обновлять эту переменную. Последний аргумент методаtransitionCompleted:
может сказать вам, выполнил ли пользователь переход по переворачиванию страницы или нет.источник
Что касается iOS 6, я обнаружил, что
viewControllers
свойство UIPageViewController постоянно обновляется, поэтому оно всегда будет содержать один контроллер представления, представляющий текущую страницу, и ничего больше. Таким образом, вы можете получить доступ к текущей странице, позвонив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; }
Но обратите внимание на комментарии, что это ненадежно.
источник
viewControllers
свойству и получаю нужные контроллеры представления, но когда я вращаюсь в методеspineLocationForInterfaceOrientation
, я больше не получаю правильные контроллеры представления. Поэтому я оставил собственное свойство currentPage вместо того, чтобы всегда полагаться наviewControllers
.К сожалению, все вышеперечисленные методы мне не помогли. Тем не менее, я нашел решение с помощью тегов. Может быть, это не лучший вариант, но он работает и надеюсь кому-то поможет:
- (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 }
Пример: суть
источник
Основываясь на ответе Оле…
Вот как я реализовал 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; }
источник
self.nextIndex = self.currentIndex
а неself.nextIndex = 0
для того, чтобы правильно восстанавливать индекс, когда переход не завершен. В противном случае вы рискуете получитьcurrentIndex
значение 0 при быстром перелистывании назад и вперед где-нибудь в середине ваших страниц.Приведенное ниже решение сработало для меня.
Apple могла бы избежать множества хлопот, сделав нативную разбивку на страницы UIPageViewController более настраиваемой. Мне пришлось прибегнуть к наложению нового 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; }
источник
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 } }
источник
pageViewController.viewControllers
. Откуда вы знаете, что viewControllers? .Первый, о котором идет речь?Я отслеживаю индекс страницы, используя небольшую функцию и определяя pageIndex как статический NSInteger.
-(void) setPageIndex { DataViewController *theCurrentViewController = [self.pageViewController.viewControllers objectAtIndex:0]; pageIndex = [self.modelController indexOfViewController:theCurrentViewController]; }
и вызов
[self setPageIndex];
внутри функции, указанной Оле, а также после обнаружения изменения ориентации.источник
Сначала я использовал решение Кори, но оно не работало на iOS5, а затем я использовал,
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed{ if(completed) { _currentViewController = [pageViewController.viewControllers lastObject]; } }
Он пробовал переключаться между разными страницами и пока работает хорошо.
источник
К сожалению, у меня ничего не работает.
У меня есть два контроллера представления, и когда я немного (около 20 пикселей) прокручиваю последнее представление назад, он запускает делегата:
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 обновляется из отображаемого UIViewController в viewWillAppear
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 }
источник
Я уже давно использую
view.tag
, пытаться отслеживать текущую страницу было слишком сложно.В этом коде индекс хранится в
tag
свойстве каждогоview
и используется для выборки следующего или предыдущего VC. С помощью этого метода также можно создать бесконечную прокрутку. Ознакомьтесь с комментарием в коде, чтобы также просмотреть это решение: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 } }
источник
Спасибо за ваш ответ, ребята, я столкнулся с аналогичной проблемой, пришлось хранить index. Я немного модифицирую свой код, вставляю его ниже:
- (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), который демонстрирует, как это делается, реализуя простой учебник по перемещению изображений. Комментарии в самом коде:
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 } }
источник
У меня есть массив 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 } }
Важно отметить здесь, что setViewControllers метод UIPageViewController не дает какой - либо делегат обратного вызова. Обратные вызовы делегатов представляют только действия пользователя касания в UIPageViewController .
источник
Это решение, которое я придумал:
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
Не забудьте установить начальный заголовок
источник
Самый простой способ приблизиться к этому 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 } }
источник
Как насчет того, чтобы запросить
viewController
прямо изUIPageViewController
(версия Swift 4):fileprivate weak var currentlyPresentedVC: UIViewController? func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { currentlyPresentedVC = pageViewController.viewControllers?.first }
Или, если вам просто нужен текущий контроллер представления в какой-то момент времени, просто используйте
pageViewController.viewControllers?.first
в это время.источник
В быстром 5 и следующем ответе sirvine
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 встроен
источник
UIViewController *viewController = [pageViewController.viewControllers objectAtIndex:0]; NSUInteger currentIndex = [(ViewController*) viewController indexNumber];
Он вернет текущий индекс страницы. и должен использовать этот код в функции делегата UIPageViewController (didFinishAnimating).
источник