iOS Swift: UIPageViewController - Turning page programmatically
Use this funtion and set the transition style for the animation you want.
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
Masking Uiview/Uiimageview to Cutout Transparent Text
Why Can't I Change Variables in a Protocol Extension Where Self Is a Class
Why Does Swift Provide Both a Cgrect Initializer and a Cgrectmake Function
Why Do I Still Need to Unwrap Swift Dictionary Value
Enum with Identical Cases Names with Associated Values of Different Types
Cannot Read the Nfc Chip of the Epassport Using iOS13
What Is Other Option Available in Swift Instead of Refactoring and Renaming Class or Attribute Name
Nstextalignment.Justified for Uilabel Does Not Work
How to Get Row Index in Swiftui List
Macos/Swift Capture Audio with Avcapturesession
Create Skscene Subclasses Programmatically, Without Size Info
Is String Type a Class or a Struct? or Something Else
Leading Zeros for Float in Swift
At Runtime, How Does Swift Know Which Implementation to Use