Skąd wiesz, jaka jest bieżąca strona / widok wyświetlany w środku UIPageViewController
?
Zastąpiłem viewDidAppear
metodę moich widoków podrzędnych, aby wysyłały identyfikator do widoku nadrzędnego w swojej viewDidAppear
metodzie.
Jednak problem jest następujący: nie mogę niezawodnie używać tego identyfikatora jako identyfikatora wyświetlanej strony. ponieważ jeśli użytkownik przewróci stronę, ale w połowie zdecyduje się zatrzymać przewracanie i odłożyć stronę z powrotem, viewDidAppear
zostanie już wywołany. (widok jest widoczny za zawiniętą stroną).
Może powinienem przełączyć się na nowy identyfikator tylko wtedy, gdy obecny widok zniknie. Ale zastanawiam się, czy nie ma prostszego sposobu na przywrócenie widoku, który jest obecnie widoczny?
viewDidAppear:animated:
zamiast tego użyć ?Odpowiedzi:
Należy ręcznie śledzić bieżącą stronę. Metoda delegata
pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:
powie Ci, kiedy zaktualizować tę zmienną. Ostatni argument metodytransitionCompleted:
może powiedzieć, czy użytkownik ukończył zmianę strony, czy nie.źródło
Od iOS 6 odkryłem, że
viewControllers
właściwość UIPageViewController jest stale aktualizowana, dzięki czemu zawsze będzie zawierał jeden kontroler widoku, który reprezentuje bieżącą stronę i nic więcej. W ten sposób możesz uzyskać dostęp do bieżącej strony, wywołującviewControllers[0]
(zakładając, że w danym momencie jest wyświetlany tylko jeden kontroler widoku).Tablica viewController aktualizuje się tylko wtedy, gdy strona „zablokuje się” na miejscu, więc jeśli użytkownik zdecyduje się częściowo odsłonić następną stronę, nie stanie się ona stroną „bieżącą”, chyba że dokończy przejście.
Jeśli chcesz śledzić „numery stron”, przypisz kontrolerom widoku wartość indeksu podczas ich tworzenia za pomocą metod źródła danych UIPageViewController.
Na przykład:
-(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; }
Ale zwróć uwagę na komentarze, że jest to niewiarygodne.
źródło
viewControllers
właściwości i otrzymuję odpowiednie kontrolery widoku, ale kiedy obracam w metodziespineLocationForInterfaceOrientation
, nie otrzymuję już poprawnych kontrolerów widoku. Dlatego trzymałem się własnej właściwości currentPage zamiast zawsze polegać naviewControllers
.Niestety wszystkie powyższe metody mi nie pomogły. Niemniej jednak znalazłem rozwiązanie za pomocą tagów. Może nie jest to najlepsze, ale działa i mam nadzieję, że komuś pomoże:
- (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; } }
In Swift: (dzięki @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 }
Przykład: gist
źródło
Opierając się na odpowiedzi Ole…
Oto jak zaimplementowałem 4 metody śledzenia bieżącej strony i aktualizowania wskaźnika strony do prawidłowego indeksu:
- (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; }
źródło
self.nextIndex = self.currentIndex
raczej niżself.nextIndex = 0
w celu prawidłowego przywrócenia indeksu, gdy przejście nie zostało zakończone. W przeciwnym razie ryzykujesz, że skończysz zcurrentIndex
wartością 0 podczas szybkiego przewijania w przód iw tył gdzieś pośrodku stron.Poniższe rozwiązanie zadziałało dla mnie.
Firma Apple mogłaby uniknąć wielu problemów, zwiększając konfigurowalność natywnej paginacji widoku przewijania UIPageViewController. Musiałem uciekać się do nakładania nowych UIView i UIPageControl tylko dlatego, że natywna paginacja UIPageViewController nie obsługuje przezroczystego tła ani zmiany położenia w ramce widoku.
- (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; }
źródło
Szybki 4
Bez zbędnego kodu. 3 sposoby na zrobienie tego. Korzystanie z metody 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 } }
źródło
pageViewController.viewControllers
. Skąd wiesz, że viewControllers?. To ten, o którym mowa?Śledzę indeks strony, używając małej funkcji i określając pageIndex jako statyczny NSInteger.
-(void) setPageIndex { DataViewController *theCurrentViewController = [self.pageViewController.viewControllers objectAtIndex:0]; pageIndex = [self.modelController indexOfViewController:theCurrentViewController]; }
i wywołanie
[self setPageIndex];
wewnątrz funkcji określonej przez Ole, a także po wykryciu zmiany orientacji.źródło
Najpierw użyłem rozwiązania Corey, ale nie działało na iOS5, a potem skończyło się na,
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed{ if(completed) { _currentViewController = [pageViewController.viewControllers lastObject]; } }
Próbował przełączać się między różnymi stronami i na razie działa dobrze.
źródło
Niestety nic powyżej nie działa.
Mam dwa kontrolery widoku i kiedy nieznacznie (około 20px) przewijam ostatni widok do tyłu, uruchamia delegata:
pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:
i mówiąc, że bieżąca strona (indeks) to
0
jest nieprawidłowa.Używanie delegata wewnątrz child viewController coś takiego:
- (void)ViewController:(id)VC didShowWithIndex:(long)page; // and a property @property (nonatomic) NSInteger index;
który jest uruchamiany wewnątrz,
viewDidAppear
jak:- (void)viewDidAppear:(BOOL)animated { ... [self.delegate ViewController:self didShowWithIndex:self.index]; }
Pracował dla mnie.
źródło
U mnie to działa niezawodnie
Mam niestandardowy UIPageController. Ta stronaController.currentPage jest aktualizowana na podstawie wyświetlanego elementu UIViewController w pliku 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 }
źródło
Używam już
view.tag
od jakiegoś czasu, próba śledzenia bieżącej strony była zbyt skomplikowana.W tym kodzie indeks jest przechowywany we
tag
właściwości każdegoview
i służy do pobierania następnego lub poprzedniego VC. Za pomocą tej metody można również stworzyć nieskończony zwój. Sprawdź komentarz w kodzie, aby wyświetlić również to rozwiązanie: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 } }
źródło
Dziękuję za odpowiedź chłopaki, napotkałem podobny problem, musiałem przechowywać indeks. Lekko modyfikuję kod, wklejam go poniżej:
- (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]; }
źródło
- (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
źródło
Poniżej kod demonstracyjny (w Swift 2), który demonstruje, jak to się robi, implementując prosty samouczek do przesuwania obrazu. Komentarze w samym kodzie:
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 } }
źródło
Mam tablicę viewControllers , którą wyświetlam w 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 } }
Ważną rzeczą jest, aby zauważyć, że setViewControllers metoda UIPageViewController nie daje żadnej zwrotnego delegata. Wywołania zwrotne pełnomocnika reprezentują tylko akcje dotykowe użytkownika w UIPageViewController .
źródło
Oto rozwiązanie, które wymyśliłem:
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) } }
Stosowanie:
let pageViewController = UIPageViewController() let delegate = DefaultUIPageViewControllerDelegate() delegate.didTransitionToViewControllerCallback = { pageViewController.title = $0.title } pageViewController.title = viewControllers.first?.title pageViewController.delegate = delegate
Upewnij się, że ustawiłeś tytuł początkowy
źródło
Najprostszym sposobem podejścia do tego IMHO jest użycie PageControl do przechowywania potencjalnego wyniku przejścia, a następnie przywrócenie go, jeśli przejście zostało anulowane. Oznacza to, że kontrolka strony zmienia się, gdy tylko użytkownik zacznie przesuwać, co jest w porządku. Wymaga to posiadania własnej tablicy UIViewControllers (w tym przykładzie o nazwie
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 } }
źródło
Co powiesz na zapytanie
viewController
bezpośrednio zUIPageViewController
(wersja Swift 4):fileprivate weak var currentlyPresentedVC: UIViewController? func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { currentlyPresentedVC = pageViewController.viewControllers?.first }
Lub, jeśli potrzebujesz tylko aktualnie prezentowanego kontrolera widoku w pewnym momencie, po prostu użyj
pageViewController.viewControllers?.first
w tym momencie.źródło
W szybkiej 5 i następującej po sirvine odpowiedzi
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 } } }
W tym przypadku nie obchodzi mnie, która klasa UIViewController jest osadzona
źródło
UIViewController *viewController = [pageViewController.viewControllers objectAtIndex:0]; NSUInteger currentIndex = [(ViewController*) viewController indexNumber];
Zwróci bieżący indeks strony. i musi używać tego kodu w ramach funkcji delegata UIPageViewController (didFinishAnimating).
źródło