UIPageViewController y guión gráfico

Estoy tratando de configurar un UIPageViewController ESPECÍFICAMENTE desde el guión gráfico:

enter image description here

TutorialPageViewController.h

 #import  @interface TutorialPageViewController : UIPageViewController  @end 

TutorialPageViewController.m

 #import "TutorialPageViewController.h" @interface TutorialPageViewController () @property (assign, nonatomic) NSInteger index; @end @implementation TutorialPageViewController { NSArray *myViewControllers; } - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.delegate = self; self.dataSource = self; [self didMoveToParentViewController:self]; UIStoryboard *tutorialStoryboard = [UIStoryboard storyboardWithName:@"TutorialStoryboard" bundle:[NSBundle mainBundle]]; UIViewController *tuto1 = [tutorialStoryboard instantiateViewControllerWithIdentifier:@"TutorialPageViewController_1"]; UIViewController *tuto2 = [tutorialStoryboard instantiateViewControllerWithIdentifier:@"TutorialPageViewController_2"]; myViewControllers = @[tuto1, tuto2, tuto1, tuto2]; self.index = 0; [self setViewControllers:@[tuto1] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil]; } - (UIViewController *)viewControllerAtIndex:(NSUInteger)index { return myViewControllers[index]; } - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { NSUInteger index = self.index; if (index == 0) { return nil; } // Decrease the index by 1 to return index--; return [self viewControllerAtIndex:index]; } - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { NSUInteger index = self.index; index++; if (index > [myViewControllers count]) { return nil; } return [self viewControllerAtIndex:index]; } - (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController { // The number of items reflected in the page indicator. return [myViewControllers count]; } - (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController { // The selected item reflected in the page indicator. return 0; } @end 

El problema es…

  • La primera página se muestra bien con el indicador de página. Mientras deslizas,
  • Puedo ver correctamente la segunda página.
  • Tan pronto como finaliza la transición, aparece una pantalla negra (con el indicador de página mostrando correctamente el número de página 2). Ya no hay interacción del usuario disponible.

Respuesta 2017 ..

Hoy en día es muy fácil hacer esto simplemente usando Storyboard .

Este tipo de “deslizar tutoriales de pantalla completa” fueron populares como “introducciones” a una aplicación por un tiempo, así que llamé a la clase debajo de IntroPages .

Paso 1, haz una vista de contenedor que sea un UIPageViewController.

Si es nuevo en iOS, aquí hay un tutorial de vista de contenedor .

(Nota: si no sabe cómo cambiar la vista de contenedor a un UIPageViewController, desplácese hacia abajo a la sección “Cómo cambiar …” en ese tutorial.

enter image description here )

Puede hacer que el contenedor tenga la forma que desee . Al igual que con cualquier vista de contenedor, puede ser una pantalla completa o una pequeña parte de la pantalla, lo que usted desee.

Paso 2,

Cree cuatro controladores de visualización sencillos que pueden ser lo que desee : imágenes, texto, tablas, cualquier cosa. (Púrpura en el ejemplo)

cuatro controladores

Tenga en cuenta que simplemente se sientan allí en su guión gráfico , no los vincule a nada.

Paso 3, debe establecer los ID de esas cuatro páginas. “id1”, “id2”, “id4”, “id4” está bien.

establecer los ID

Paso 4, copia y pega! Aquí está la clase IntroPages,

 class IntroPages: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate { var pages = [UIViewController]() override func viewDidLoad() { super.viewDidLoad() self.delegate = self self.dataSource = self let p1: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "id1") let p2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "id2") let p3: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "id3") // etc ... pages.append(p1) pages.append(p2) pages.append(p3) // etc ... setViewControllers([p1], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil) } func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController)-> UIViewController? { let cur = pages.index(of: viewController)! // if you prefer to NOT scroll circularly, simply add here: // if cur == 0 { return nil } let prev = abs((cur - 1) % pages.count) return pages[prev] } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController)-> UIViewController? { let cur = pages.index(of: viewController)! // if you prefer to NOT scroll circularly, simply add here: // if cur == (pages.count - 1) { return nil } let nxt = abs((cur + 1) % pages.count) return pages[nxt] } func presentationIndex(for pageViewController: UIPageViewController)-> Int { return pages.count } } 

(Mire los comentarios: hay código para el bucle o paginación lineal como prefiera).

Eso es todo, hay que terminar.

Simplemente configura el estilo de transición en el guión gráfico,

enter image description here

es muy probable que desee “Desplazar”, no el otro.

Para alguien, que quiere ver el desplazamiento de la página de trabajo (hacia adelante / hacia atrás)

 -(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { NSUInteger currentIndex = [myViewControllers indexOfObject:viewController]; // get the index of the current view controller on display if (currentIndex > 0) { return [myViewControllers objectAtIndex:currentIndex-1]; // return the previous viewcontroller } else { return nil; // do nothing } } -(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { NSUInteger currentIndex = [myViewControllers indexOfObject:viewController]; // get the index of the current view controller on display // check if we are at the end and decide if we need to present // the next viewcontroller if (currentIndex < [myViewControllers count]-1) { return [myViewControllers objectAtIndex:currentIndex+1]; // return the next view controller } else { return nil; // do nothing } } 

Solo para agregar a esta excelente respuesta de EditOR, esto es lo que debes hacer si prefieres la paginación de "vuelta y vuelta": seguir usando la misma técnica de EditOR

 -(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { NSUInteger currentIndex = [myViewControllers indexOfObject:viewController]; --currentIndex; currentIndex = currentIndex % (myViewControllers.count); return [myViewControllers objectAtIndex:currentIndex]; } -(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { NSUInteger currentIndex = [myViewControllers indexOfObject:viewController]; ++currentIndex; currentIndex = currentIndex % (myViewControllers.count); return [myViewControllers objectAtIndex:currentIndex]; } 

Extendiendo la respuesta de Joe Blow con el código Swift para la clase UIPageViewController :

 import UIKit class MyPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate { var pages = [UIViewController]() override func viewDidLoad() { super.viewDidLoad() self.delegate = self self.dataSource = self let page1: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("page1") let page2: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("page2") pages.append(page1) pages.append(page2) setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil) } func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? { let currentIndex = pages.indexOf(viewController)! let previousIndex = abs((currentIndex - 1) % pages.count) return pages[previousIndex] } func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? { let currentIndex = pages.indexOf(viewController)! let nextIndex = abs((currentIndex + 1) % pages.count) return pages[nextIndex] } func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int { return pages.count } func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int { return 0 } } 

Obtenga más información sobre el uso de UIPageViewController con vista de contenedor con la configuración del guión gráfico.

Parece que hay muchas preguntas sobre UIPageViewController en Storyboard.

Aquí hay un código de demostración para mostrarle cómo puede usar UIPageViewController en el guión gráfico como una vista de pantalla completa independiente o como un UIContainerView, si solo desea visualizar un área pequeña de su pantalla.

enter image description here enter image description here enter image description here

Actualizado para Swift 3:

 class YourPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate { var pages = [UIViewController]() override func viewDidLoad() { super.viewDidLoad() self.delegate = self self.dataSource = self let page1: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "page1") let page2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "page2") pages.append(page1) pages.append(page2) setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil) } func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { let currentIndex = pages.index(of: viewController)! let previousIndex = abs((currentIndex - 1) % pages.count) return pages[previousIndex] } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { let currentIndex = pages.index(of: viewController)! let nextIndex = abs((currentIndex + 1) % pages.count) return pages[nextIndex] } func presentationIndex(for pageViewController: UIPageViewController) -> Int { return pages.count } } 

Para tener un desplazamiento infinito usando la respuesta de @samwize, debe agregar un condicional para verificar si hay valores negativos. De lo contrario, simplemente cambia entre la primera y la segunda página. Esto solo es necesario si planea tener más de dos páginas.

 func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { let currentIndex = pages.index(of: viewController)! var previousIndex = (currentIndex - 1) % pages.count if previousIndex < 0 {previousIndex = pages.count - 1} return pages[previousIndex] } 

El problema es que estás reutilizando UIViewController instancias de UIViewController :

 myViewControllers = @[tuto1, tuto2, tuto1, tuto2]; 

Le sugiero que tenga un NSMutableSet que sirva como un conjunto de instancias UIViewController reutilizables.

En viewControllerBeforeViewController: y viewControllerBeforeViewController: busque su NSMutableSet usando NSPredicate para encontrar un UIViewController con parentViewController igual a nil . Si encuentra uno, devuélvalo. De lo contrario, cree una instancia nueva, agréguelo al NSMutableSet y luego devuélvalo.

Cuando haya terminado y sus pruebas estén aprobadas, puede extraer el grupo en su propia clase.