How to Provide Default Implementations for Uipageviewcontrollerdatasource

iOS Swift: UIPageViewController - Turning page programmatically

Use this funtion and set the transition style for the animation you want.

Sample Image

Sample Image

Swift: Type 'ViewController' does not conform to protocol 'UIPageViewControllerDataSource'

It's because the UIPageViewControllerDataSource protocol has updated method signatures - you're using:

func pageViewController(pageViewController: UIPageViewController!, viewControllerBeforeViewController viewController: UIViewController!) -> UIViewController!

func pageViewController(pageViewController: UIPageViewController!, viewControllerAfterViewController viewController: UIViewController!) -> UIViewController!

but now they are:

func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?

func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController?

When you're in doubt on a non conforming protocol, a Command+Click on the protocol name will bring you to the protocol declaration, where you can see if you are implementing its interface correctly

Issue Implementing UIPageViewController

For anybody who wants the UIScrollView to don't show the viewUnder when scrolling there are no existent views to scroll.

scrollView.showsLargeContentViewer = false

UIPageViewController Implementation - Black Screen After Changing View Controllers

Saving index as property in UIPageViewController din't work for me as well. To solve this I had to create a base UIViewController with index property as follow:

class PageItemController: UIViewController {

var index = -1

override func viewDidLoad() {
super.viewDidLoad()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

}

Then make all your pages subclasses of PageItemController and set their indexes corresponding to their position in array

func initControllers(){
registrationController = self.storyboard?.instantiateViewControllerWithIdentifier("RegistrationViewController") as PageItemController
registrationController.index = 0
loginController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginViewController") as PageItemController
loginController.index = 1
captureController = self.storyboard?.instantiateViewControllerWithIdentifier("CaptureViewController") as PageItemController
captureController.index = 2
// visibleVCS type must be [PageItemCotroller]
visibleVCS = [registrationController, loginController, captureController]
}

And finally implement before and after methods based on currentController index

func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {

let itemController = viewController as! PageItemController

if itemController.index > 0 {

return visibleVCS[itemController.index - 1]

}

return nil

}


func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {

let itemController = viewController as! PageItemController

if itemController.index < visibleVCS.count - 1 {


return visibleVCS[itemController.index + 1]
}

return nil
}

How to add a new view controller in UIPageViewController after UIButton was tapped?

The UIPageViewController uses a datasource to obtain the next or previous view controller. So the solution is to add your new view controller to the datasource once the button is touched.

Say your datasource is implemented as an NSArray (see the XCode's Page-based Application template for an example of this) you just add your new vc to that array and set the new vc by calling [UIPageViewController setViewControllers:direction:animated:completion:].

Since you didn't provide any details on how you implemented your hierarchy I can't be more specific.

Edit:

Now that I have some code, here's what I'd do:

First I wouldn't save the labelContents but rather the view controller itself in the modelArray. Besides other things, your current design creates a new ContentVC every time you change pages. That's a lot of unnecessary overhead. Here's how I would implement that:

(btw, you should think of a more descriptive name than labelContents. Right now it might be fine if there's just one label, but what if you add more labels in the future?)

- (ContentVC *)contentViewControllerWithLabelContents:(NSString *)labelContents
{
ContentVC *vc = [[ContentVC alloc] initWithNibName:@"ContentVC" bundle:nil];
vc.labelContents = labelContents;
return vc;
}

- (void)viewDidLoad
{
NSMutableArray *vcs = [NSMutableArray array];
[vcs addObject:[self contentViewControllerWithLabelContents:@"Page One"]];
[vcs addObject:[self contentViewControllerWithLabelContents:@"Page Two"]];
//...
self.modelArray = vcs;
}


#pragma mark - UIPageViewControllerDataSource Methods

// Returns the view controller before the given view controller. (required)
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger currentIndex = [self.modelArray indexOfObject:viewController];
if(currentIndex == 0)
return nil;

ContentVC *cVC = [self.modelArray objectAtIndex:currentIndex - 1];
return cVC;
}

// Returns the view controller after the given view controller. (required)
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger currentIndex = [self.modelArray indexOfObject:viewController];
if(currentIndex == self.modelArray.count - 1)
return nil;

ContentVC *cVC = [self.modelArray objectAtIndex:currentIndex + 1];
return cVC;
}

The code in your comment:

- (IBAction)newButtonTapped:(id)sender 
{
if(sender){
[self.modelArray addObject:@"Page Two"];
ContentVC *newVC = [[ContentVC alloc] initWithNibName:@"ContentVC" bundle:nil];
NSArray *arr = [NSArray arrayWithObject:newVC];
[self.pageVC setViewControllers:arr direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
NSLog(@"%@", self.modelArray);
}
}

doesn't work because you didn't set the labelContents of the ContentVC. So you basically end up calling [self.modelObject indexOfObject:nil] or [self.modelObject indexOfObject:@""] depending on your implementation of labelContents. Since neither of them is in the array, the call returns NSNotFound which on 64bit systems is translated to NSIntegerMax and that is 2147483647. Later you try to access your modelArray at index NSIntegerMax - 1 and that raises an NSRangeException.

So can fix that by either setting the labelContents in your newButtonTapped: method or if you follow my suggestion to redesign your code:

- (IBAction)newButtonTapped:(id)sender 
{
if(sender){
ContentVC *newVC = [self contentViewControllerWithLabelContents:@"Page Two"];
[self.modelArray addObject:newVC];

NSArray *newVCs = nil;

if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) || self.modelArray.count == 1)
newVCs = @[newVC];
else
{
NSUInteger secondLastIndex = self.modelArray.count - 2;
id previousVC = [self.modelArray objectAtIndex:secondLastIndex];
newVCs = @[previousVC, newVC];
}

[self.pageVC setViewControllers:newVCs direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}
}

Turn page programmatically in UIPageViewController when using UIPageViewControllerDataSource?

The Method mentioned in the answer ( setViewControllers:direction:animated:completion: ) sets the current view controller and is exactly what you need.
With the first param you define the view controller(s), you want to display. Use one if you don't have a spine, if you book is left/right use two.



Related Topics



Leave a reply



Submit