Odpowiedzi:
Tak, jest to możliwe metodą:
- (void)setViewControllers:(NSArray *)viewControllers
direction:(UIPageViewControllerNavigationDirection)direction
animated:(BOOL)animated
completion:(void (^)(BOOL finished))completion;`
Jest to ta sama metoda, która jest używana do konfigurowania pierwszego kontrolera widoku na stronie. Podobnie, możesz go użyć, aby przejść do innych stron.
Zastanawiasz się, dlaczego viewControllers
to tablica, a nie pojedynczy kontroler widoku?
Dzieje się tak, ponieważ kontroler widoku strony może mieć „kręgosłup” (jak w iBookach), wyświetlający jednocześnie 2 strony treści. Jeśli wyświetlasz 1 stronę treści na raz, po prostu przekaż 1-elementową tablicę.
Przykład w Swift:
pageContainer.setViewControllers([displayThisViewController], direction: .Forward, animated: true, completion: nil)
Ponieważ również tego potrzebowałem, omówię bardziej szczegółowo, jak to zrobić.
Uwaga: Zakładam, że użyłeś standardowego formularza szablonu do wygenerowania UIPageViewController
struktury - który ma zarówno plik, jak modelViewController
i dataViewController
utworzony, gdy go wywołasz. Jeśli nie rozumiesz, co napisałem - wróć i utwórz nowy projekt, który używa rozszerzeniaUIPageViewController
będzie podstawą. Wtedy zrozumiesz.
Dlatego przejście do konkretnej strony wymaga skonfigurowania różnych elementów metody wymienionej powyżej. W tym ćwiczeniu przyjmuję, że jest to widok poziomy z dwoma widokami. Zaimplementowałem to również jako IBAction, aby można było to zrobić za pomocą naciśnięcia przycisku lub czegokolwiek - równie łatwo jest wywołać selektor i przekazać potrzebne elementy.
Tak więc w tym przykładzie potrzebujesz dwóch kontrolerów widoku, które zostaną wyświetlone - i opcjonalnie, niezależnie od tego, czy idziesz do przodu w książce, czy do tyłu.
Zwróć uwagę, że po prostu zakodowałem na stałe, gdzie przejść do stron 4 i 5 i używam przesunięcia w przód. Stąd widać, że wszystko, co musisz zrobić, to przekazać zmienne, które pomogą ci zdobyć te przedmioty ...
-(IBAction) flipToPage:(id)sender {
// Grab the viewControllers at position 4 & 5 - note, your model is responsible for providing these.
// Technically, you could have them pre-made and passed in as an array containing the two items...
DataViewController *firstViewController = [self.modelController viewControllerAtIndex:4 storyboard:self.storyboard];
DataViewController *secondViewController = [self.modelController viewControllerAtIndex:5 storyboard:self.storyboard];
// Set up the array that holds these guys...
NSArray *viewControllers = nil;
viewControllers = [NSArray arrayWithObjects:firstViewController, secondViewController, nil];
// Now, tell the pageViewContoller to accept these guys and do the forward turn of the page.
// Again, forward is subjective - you could go backward. Animation is optional but it's
// a nice effect for your audience.
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
// Voila' - c'est fin!
}
Oto inny przykład kodu dla widoku pojedynczej strony. Implementacja dla viewControllerAtIndex została tutaj pominięta, powinna zwrócić prawidłowy kontroler widoku dla danego indeksu.
- (void)changePage:(UIPageViewControllerNavigationDirection)direction {
NSUInteger pageIndex = ((FooViewController *) [_pageViewController.viewControllers objectAtIndex:0]).pageIndex;
if (direction == UIPageViewControllerNavigationDirectionForward) {
pageIndex++;
}
else {
pageIndex--;
}
FooViewController *viewController = [self viewControllerAtIndex:pageIndex];
if (viewController == nil) {
return;
}
[_pageViewController setViewControllers:@[viewController]
direction:direction
animated:YES
completion:nil];
}
Strony można zmieniać, dzwoniąc
[self changePage:UIPageViewControllerNavigationDirectionReverse];
[self changePage:UIPageViewControllerNavigationDirectionForward];
Mogę czegoś tutaj całkowicie brakować, ale to rozwiązanie wydaje się o wiele prostsze niż wiele innych proponowanych.
extension UIPageViewController {
func goToNextPage(animated: Bool = true, completion: ((Bool) -> Void)? = nil) {
if let currentViewController = viewControllers?[0] {
if let nextPage = dataSource?.pageViewController(self, viewControllerAfter: currentViewController) {
setViewControllers([nextPage], direction: .forward, animated: animated, completion: completion)
}
}
}
}
Ten kod dobrze mi działał, dzięki.
Oto, co z nim zrobiłem. Niektóre metody przechodzenia do przodu lub do tyłu i jedna do przechodzenia bezpośrednio do określonej strony. Jest to 6-stronicowy dokument w widoku pionowym. Będzie działać dobrze, jeśli wkleisz go do implementacji RootController szablonu pageViewController.
-(IBAction)pageGoto:(id)sender {
//get page to go to
NSUInteger pageToGoTo = 4;
//get current index of current page
DataViewController *theCurrentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
NSUInteger retreivedIndex = [self.modelController indexOfViewController:theCurrentViewController];
//get the page(s) to go to
DataViewController *targetPageViewController = [self.modelController viewControllerAtIndex:(pageToGoTo - 1) storyboard:self.storyboard];
DataViewController *secondPageViewController = [self.modelController viewControllerAtIndex:(pageToGoTo) storyboard:self.storyboard];
//put it(or them if in landscape view) in an array
NSArray *theViewControllers = nil;
theViewControllers = [NSArray arrayWithObjects:targetPageViewController, secondPageViewController, nil];
//check which direction to animate page turn then turn page accordingly
if (retreivedIndex < (pageToGoTo - 1) && retreivedIndex != (pageToGoTo - 1)){
[self.pageViewController setViewControllers:theViewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
}
if (retreivedIndex > (pageToGoTo - 1) && retreivedIndex != (pageToGoTo - 1)){
[self.pageViewController setViewControllers:theViewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:NULL];
}
}
-(IBAction)pageFoward:(id)sender {
//get current index of current page
DataViewController *theCurrentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
NSUInteger retreivedIndex = [self.modelController indexOfViewController:theCurrentViewController];
//check that current page isn't first page
if (retreivedIndex < 5){
//get the page to go to
DataViewController *targetPageViewController = [self.modelController viewControllerAtIndex:(retreivedIndex + 1) storyboard:self.storyboard];
//put it(or them if in landscape view) in an array
NSArray *theViewControllers = nil;
theViewControllers = [NSArray arrayWithObjects:targetPageViewController, nil];
//add page view
[self.pageViewController setViewControllers:theViewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
}
}
-(IBAction)pageBack:(id)sender {
//get current index of current page
DataViewController *theCurrentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
NSUInteger retreivedIndex = [self.modelController indexOfViewController:theCurrentViewController];
//check that current page isn't first page
if (retreivedIndex > 0){
//get the page to go to
DataViewController *targetPageViewController = [self.modelController viewControllerAtIndex:(retreivedIndex - 1) storyboard:self.storyboard];
//put it(or them if in landscape view) in an array
NSArray *theViewControllers = nil;
theViewControllers = [NSArray arrayWithObjects:targetPageViewController, nil];
//add page view
[self.pageViewController setViewControllers:theViewControllers direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:NULL];
}
}
Swift 4:
Musiałem przejść do następnego kontrolera, gdy dotknę dalej kontrolera widoku kontrolera stron, a także zaktualizowałem indeks pageControl, więc było to dla mnie najlepsze i najprostsze rozwiązanie :
let pageController = self.parent as! PageViewController
pageController.setViewControllers([parentVC.orderedViewControllers[1]], direction: .forward, animated: true, completion: nil)
pageController.pageControl.currentPage = 1
A co z używaniem metod z dataSource?
UIViewController *controller = [pageViewController.dataSource pageViewController:self.pageViewController viewControllerAfterViewController:pageViewController.viewControllers.firstObject];
[pageViewController setViewControllers:@[controller] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
Analogicznie do tyłu.
W Swift :
import UIKit
extension HomeViewController {
// MARK: - Carousel management.
func startTimerForMovingCarousel(let numberOfSecondsBetweenFiringsOfTheTimer: NSTimeInterval) {
if (self.timerForMovingCarousel == nil) {
self.timerForMovingCarousel = NSTimer.scheduledTimerWithTimeInterval(numberOfSecondsBetweenFiringsOfTheTimer,
target: self,
selector: "moveCarousel",
userInfo: nil,
repeats: true)
}
}
func moveCarousel() {
println("I move the carousel.")
let currentContentViewControllerDisplayed = self.pageViewController!.viewControllers[0] as! ContentViewController
var index: Int = currentContentViewControllerDisplayed.index
if index == self.arrayViewControllers.count - 1 {
index = 0
} else {
++index
}
let contentViewControllerToDisplay = self.arrayViewControllers[index] as! ContentViewController
self.pageViewController.setViewControllers([contentViewControllerToDisplay],
direction: UIPageViewControllerNavigationDirection.Forward,
animated: true,
completion: nil)
self.pageControl.currentPage = contentViewControllerToDisplay.index
}
func invalidateTimerForMovingCarousel() {
if (self.timerForMovingCarousel != nil) {
self.timerForMovingCarousel.invalidate()
self.timerForMovingCarousel = nil
}
}
}
Jednak użycie tego wywołania metody „self.pageViewController setViewControllers” nie powoduje wywołania „viewDidDissapear” w kontrolerze viewController dla strony, która została odrzucona, podczas gdy ręczne obrócenie strony powoduje wywołanie viewDidDissapear na odwróconej stronie. Myślę, że to jest przyczyną mojej katastrofy
„Liczba dostarczonych kontrolerów widoku (0) nie jest zgodna z liczbą wymaganą (1) dla żądanego przejścia”
Potrzebowałem również przycisku do nawigacji w lewo na kontrolerze PageViewController, który powinien robić dokładnie to, czego można oczekiwać od ruchu przeciągnięcia.
Ponieważ miałem już kilka reguł w " pageViewController: viewControllerBeforeViewController " do obsługi naturalnej nawigacji po przesunięciu, chciałem, aby przycisk ponownie wykorzystał cały ten kod i po prostu nie mogłem sobie pozwolić na użycie określonych indeksów aby dotrzeć do stron, jak poprzednio odpowiedzi tak. Musiałem więc wybrać alternatywne rozwiązanie.
Należy pamiętać, że poniższy kod dotyczy kontrolera widoku strony, który jest właściwością wewnątrz mojego niestandardowego kontrolera ViewController i ma kręgosłup ustawiony na środek.
Oto kod, który napisałem dla mojego przycisku Nawiguj w lewo:
- (IBAction)btnNavigateLeft_Click:(id)sender {
// Calling the PageViewController to obtain the current left page
UIViewController *currentLeftPage = [_pageController.viewControllers objectAtIndex:0];
// Creating future pages to be displayed
NSArray *newDisplayedPages = nil;
UIViewController *newRightPage = nil;
UIViewController *newLeftPage = nil;
// Calling the delegate to obtain previous pages
// My "pageViewController:viewControllerBeforeViewController" method returns nil if there is no such page (first page of the book).
newRightPage = [self pageViewController:_pageController viewControllerBeforeViewController:currentLeftPage];
if (newRightPage) {
newLeftPage = [self pageViewController:_pageController viewControllerBeforeViewController:newRightPage];
}
if (newLeftPage) {
newDisplayedPages = [[NSArray alloc] initWithObjects:newLeftPage, newRightPage, nil];
}
// If there are two new pages to display, show them with animation.
if (newDisplayedPages) {
[_pageController setViewControllers:newDisplayedPages direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:nil];
}
}
Możesz zrobić coś bardzo podobnego, aby utworzyć właściwy przycisk nawigacyjny z tego miejsca.
W przypadku, gdy KONTROLA STRONY wskazująca bieżącą stronę jest O && Chcesz, aby strony nawigowały za pomocą funkcji przewijania, a także poprzez klikanie niestandardowych przycisków w Kontrolerze widoku strony, poniższe mogą być pomocne.
//Set Delegate & Data Source for PageView controller [Say in View Did Load]
self.pageViewController.dataSource = self;
self.pageViewController.delegate = self;
// Delegates called viewControllerBeforeViewController when User Scroll to previous page
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController{
[_nextBtn setTitle:@"Next" forState:UIControlStateNormal];
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;
if (index == NSNotFound){
return nil;
}else if (index > 0){
index--;
}else{
return nil;
}
return [self viewControllerAtIndex:index];
}
// Deleguj o nazwie viewControllerAfterViewController, gdy użytkownik przewinie do następnej strony
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController{
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;
if (index == NSNotFound){
return nil;
}else if (index < 3){
index++;
}else{
return nil;
}
return [self viewControllerAtIndex:index];}
//Delegate called while transition of pages. The need to apply logic in this delegate is to maintain the index of current page.
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers{
buttonIndex = (int)((PageContentViewController*) pendingViewControllers.firstObject).pageIndex;
}
-(void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed{
if (buttonIndex == 0){
_backButton.hidden = true;
}else if (buttonIndex == [self.pageImages count] - 1){
_backButton.hidden = false;
[_nextBtn setTitle:@"Begin" forState:UIControlStateNormal];
}else{
_backButton.hidden = false;
}
}
// Logika przycisków niestandardowych (Wstecz i dalej)
-(void)backBtnClicked:(id)sender{
if (buttonIndex > 0){
buttonIndex -= 1;
}
if (buttonIndex < 1) {
_backButton.hidden = YES;
}
if (buttonIndex >=0) {
[_nextBtn setTitle:@"Next" forState:UIControlStateNormal];
PageContentViewController *startingViewController = [self viewControllerAtIndex:buttonIndex];
NSArray *viewControllers = @[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}
}
-(void)nextBtnClicked:(id)sender{
if (buttonIndex < 3){
buttonIndex += 1;
}
if(buttonIndex == _pageImages.count){
//Navigate Outside Pageview controller
}else{
if (buttonIndex ==3) {
[_nextBtn setTitle:@"Begin" forState:UIControlStateNormal];
}
_backButton.hidden = NO;
PageContentViewController *startingViewController = [self viewControllerAtIndex:buttonIndex];
NSArray *viewControllers = @[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}
}
//BUTTON INDEX & MAX COUNT
-(NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController{
return [self.pageImages count];}
-(NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController{
return buttonIndex;}
- (void)moveToPage {
NSUInteger pageIndex = ((ContentViewController *) [self.pageViewController.viewControllers objectAtIndex:0]).pageIndex;
pageIndex++;
ContentViewController *viewController = [self viewControllerAtIndex:pageIndex];
if (viewController == nil) {
return;
}
[self.pageViewController setViewControllers:@[viewController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
}
// teraz wywołaj tę metodę
[self moveToPage];
Mam nadzieję, że zadziała :)
W przypadku pojedynczej strony właśnie zredagowałem odpowiedź @Jack Humphries
-(void)viewDidLoad
{
counter = 0;
}
-(IBAction)buttonClick:(id)sender
{
counter++;
DataViewController *secondVC = [self.modelController viewControllerAtIndex:counter storyboard:self.storyboard];
NSArray *viewControllers = nil;
viewControllers = [NSArray arrayWithObjects:secondVC, nil];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
}
Jak zauważył @samwize, sposób paginacji polega na użyciu
setViewControllers:array
Oto sposób, w jaki to zrobiłem: moje źródło danych i delegat są oddzielne. Możesz wywołać tę metodę i zmienić tylko „następny” widok. Wcześniej musisz zastosować reguły stronicowania. Oznacza to, że musisz sprawdzić kierunek i następny kontroler. Jeśli użyjesz tej metody z niestandardowym źródłem danych, tablica kontrolerów, które wyświetlasz, nie ulegnie zmianie (ponieważ użyjesz niestandardowej tablicy w podklasie NSObject).
Korzystając z własnego źródła danych, metoda po prostu ustawia nowy widok (z określonym kierunkiem). Ponieważ nadal będziesz kontrolować strony, które będą wyświetlane podczas normalnego przesuwania.
Pracuję nad tym od wczoraj iw końcu to zadziałało. Nie mogłem całkowicie użyć standardowego szablonu, ponieważ mam trzy różne viewControllers jako childviews:
1 - rootViewController;
2 - model;
3 - viewControllers
3.1 - VC1;
3.2 - VC2;
3.3 - VC3.
Note: all of VCs has Storyboard ID.
Potrzebowałem przycisku wyzwalacza tylko w pierwszym VC, więc dodałem jedną właściwość dla pageViewController i modelu:
#import <UIKit/UIKit.h>
#import "Model.h"
@interface VC1 : UIViewController
@property (nonatomic, strong) UIPageViewController *thePageViewController;
@property (nonatomic, strong) SeuTimePagesModel *theModel;
@end
Przepisałem metodę init modelu, aby przekazać storyboard i pageViewController jako parametry, więc mogłem ustawić je na mój VC1:
- (id)initWithStoryboard:(UIStoryboard *)storyboard
andPageViewController:(UIPageViewController *)controller
{
if (self = [super init])
{
VC1 *vc1 = [storyboard instantiateViewControllerWithIdentifier:@"VC1"];
vc1.pageViewController = controller;
vc1.model = self;
VC2 *vc2 = [storyboard instantiateViewControllerWithIdentifier:@"VC2"];
VC3 *vc3 = [storyboard instantiateViewControllerWithIdentifier:@"VC3"];
self.pageData = [[NSArray alloc] initWithObjects:vc1, vc2, vc3, nil];
}
return self;
}
Na koniec dodałem IBAction w moim vc1, aby wyzwolić metodę pageViewController setViewControllers: direction: animated: Complete ::
- (IBAction)go:(id)sender
{
VC2 *vc2 = [_model.pages objectAtIndex:1];
NSArray *viewControllers = @[vc2];
[_pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:nil];
}
Uwaga Nie muszę wyszukiwać indeksów VC, mój przycisk przenosi mnie do mojego drugiego VC, ale nie jest trudno uzyskać wszystkie indeksy i śledzić swoje widoki dzieci:
- (IBAction)selecionaSeuTime:(id)sender
{
NSUInteger index = [_model.pages indexOfObject:self];
index++;
VC2 *vc2 = [_model.pages objectAtIndex:1];
NSArray *viewControllers = @[vc2];
[_pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:nil];
}
Mam nadzieję, że Ci to pomoże!
Oto rozwiązanie wykorzystujące metody UIPageViewControllerDataSource :
Kod śledzi bieżący wyświetlany kontroler widoku w UIPageViewController .
...
@property (nonatomic, strong) UIViewController *currentViewController;
@property (nonatomic, strong) UIViewController *nextViewController;
...
Najpierw zainicjuj currentViewController do początkowego kontrolera widoku ustawionego dla UIPageViewController .
- (void) viewDidLoad {
[super viewDidLoad];
...
self.currentViewController = self.viewControllers[0];
...
[self.pageViewController setViewControllers: @[self.currentViewController] direction: dir animated: YES completion: nil];
}
Następnie śledzić currentViewController w UIPageViewControllerDelegate metod: pageViewController: willTransitionToViewControllers: a pageViewController: didFinishAnimating: previousViewControllers: transitionCompleted: .
- (nullable UIViewController *) pageViewController: (UIPageViewController *) pageViewController viewControllerBeforeViewController: (UIViewController *) viewController {
NSInteger idx = [self.viewControllers indexOfObject: viewController];
if (idx > 0) {
return self.viewControllers[idx - 1];
} else {
return nil;
}
}
- (nullable UIViewController *) pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController: (UIViewController *) viewController {
NSInteger idx = [self.viewControllers indexOfObject: viewController];
if (idx == NSNotFound) {
return nil;
} else {
if (idx + 1 < self.viewControllers.count) {
return self.viewControllers[idx + 1];
} else {
return nil;
}
}
}
- (void) pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray<UIViewController *> *)pendingViewControllers {
self.nextViewController = [pendingViewControllers firstObject];
}
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray<UIViewController *> *)previousViewControllers transitionCompleted:(BOOL)completed {
if (completed) {
self.currentViewController = self.nextViewController;
}
}
Na koniec, w sposobie wyboru (w poniższym przykładzie jest to - (IBAction) onNext: (ID) nadawcy ), można programowo przejść do następnego lub poprzedniego kontrolera używając UIPageViewControllerDataSouce metod pageViewController: viewControllerBeforeViewController: , pageViewController: viewControllerAfterViewController: aby uzyskać następny lub poprzedni kontroler widoku i przejdź do niego, wywołując [self.pageViewController setViewControllers: @ [vc] direction: UIPageViewControllerNavigationDirectionForward animowane: YES ukończenie: nil];
- (void) onNext {
UIViewController *vc = [self pageViewController: self.tutorialViewController viewControllerAfterViewController: self.currentViewController];
if (vc != nil) {
self.currentViewController = vc;
[self.tutorialViewController setViewControllers: @[vc] direction: UIPageViewControllerNavigationDirectionForward animated: YES completion: nil];
}
}
Ponieważ ViewControllers są osadzone w PageViewController, wszystko, co musisz zrobić, to:
Przykład w Xamarin, ale funkcjonalnie podobny do Swift itp. Poniższy kod byłby w delegacie Button Click w jednym z ViewControllers wyświetlanych jako strona:
var parent = ParentViewController as WelcomePageViewController;
var next = parent.DataSource.GetNextViewController(parent, this);
parent.SetViewControllers(new UIViewController[] { next }, UIPageViewControllerNavigationDirection.Forward, true, null);