`
zhy584520
  • 浏览: 183799 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

uipageviewcontroller-programatically-without-storyboarding

    博客分类:
  • IOS
 
阅读更多

原文地址:http://www.ioslearner.com/implementing-uipageviewcontroller-programatically-without-storyboarding/

Recently I tried the new feature introduced in iOS 5.0 – The UIPageViewController. Apple has provided an already built in template for this – Page-Based Application. But the default template uses storyboarding. So, in this tutorial, we will see how to implement a UIPageViewController without storyboarding.

Ok so lets get started!

Initial Project setup:

Bump up your Xcode and create a new Project, and choose Single View Application from the template. Make sure that the device family you choose is iPad and the Use Storyboard option is unchecked as shown below.

Create a new SingleView Application projectWe will now make our view controller class which will have the content. In this simple application, we will just have a page with a label denoting the number of that page, and finally our application will look like this:

UIPageViewController application

Make a new UIViewController subclass and name it ContentViewController. Open the ContentViewController.xib and do the following:

(a) Change the background color of the view to gray color.

(b) Add a view from the objects window, and change its color as in the screenshot after point (c). Also set the autoresizing mask of the view as follows:

Autoresizing mask of view

(c) Add a label to the ContentViewController.xib. Set the autoresizing mask for the label as follows:

Autoresize mask for label

Now, Create an outlet for the label by opening the Assistant Editor and Ctrl+Drag from the label onto the ContentViewController.h as follows:

Add a label to ContentViewController Nib

(d)  Create an NSString property in ContentViewController.h as follows:

View Code OBJC
@property (strong, nonatomic) NSString *labelContents;

Also synthesize the property in the implementation. This NSString property will hold the contents of our label. And so in our viewDidLoad, we write the following:

View Code OBJC
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.myLabel.text = self.labelContents;
}

Ok. So now our project setup is complete. We now start with the UIPageViewController stuff!

UIPageViewController Implementation:

We create two properties in our ViewController.h file as follows:

View Code OBJC
@property (nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, strong) NSMutableArray *modelArray;

and synthesize them in the implementation.

We will also instantiate our modelArray, which will store the contents that will be displayed in our ContentViewController label.

View Code OBJC
self.modelArray = [[NSMutableArray alloc] init];
    for (int index = 1; index

To add the UIPageViewController into our app, we will follow these simple steps:

Step 1: Instantiate the UIPageViewController

View Code OBJC
    self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
                                                              navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];

Here, for the  transitionStyle, we give UIPageViewControllerTransitionStylePageCurl. This is the transition style when we transition between our views.

We give navigationOrientation as UIPageViewControllerNavigationOrientationHorizontal. This specifies that we want Left <-> Right direction in which the navigation of our pages will happen. If we give UIPageViewControllerNavigationOrientationVertical, the navigation will happen Top <-> Bottom, as in a wall calendar.

Last is the options dictionary. Here, we pass in as nil. We can give the default spine location here, if required.

Step 2: Assign the delegate and datasource as self. Also conform to UIPageViewControllerDelegate protocol in the ViewController.h file.

View Code OBJC
    self.pageViewController.delegate = self;
    self.pageViewController.dataSource = self;

Step 3:  Next, we set the initial view controller as follows:

View Code OBJC
    ContentViewController *contentViewController = [[ContentViewController alloc] initWithNibName:@"ContentViewController" bundle:nil];
    contentViewController.labelContents = [self.modelArray objectAtIndex:0];
    NSArray *viewControllers = [NSArray arrayWithObject:contentViewController];
    [self.pageViewController setViewControllers:viewControllers
                                      direction:UIPageViewControllerNavigationDirectionForward
                                       animated:NO
                                     completion:nil];

Here, we instantiate our ContentViewController and add it to an array. Then we assign that array using this UIPageViewController instance method:

View Code OBJC
    - (void)setViewControllers:(NSArray *)viewControllers
                     direction:(UIPageViewControllerNavigationDirection)direction
                      animated:(BOOL)animated
                    completion:(void (^)(BOOL finished))completion;

We pass in  the direction as UIPageViewControllerNavigationDirectionForward, which is again for Left <-> Right direction. We specify No for animated, because we are setting our initial viewControllers, so we don’t want to animate it. And then we specify nil for the completion block.

Step 4: We now do the ViewController containment stuff.

(a) Add the pageViewController as the childViewController of our ViewController:

View Code OBJC
[self addChildViewController:self.pageViewController];<span class="Apple-style-span" style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;"> </span>

(b) Add the view of the pageViewController to the current view:

View Code OBJC
[self.view addSubview:self.pageViewController.view];

(c) Call didMoveToParentViewController: of the childViewController, the UIPageViewController instance in our case:

View Code OBJC
[self.pageViewController didMoveToParentViewController:self];

Step 5:  Though this is not mandatory, we set the pageViewController’s frame as an inset rect, so that we are able to distinguish between our view and the pageViewController’s view.

View Code OBJC
    CGRect pageViewRect = self.view.bounds;
    pageViewRect = CGRectInset(pageViewRect, 40.0, 40.0);
    self.pageViewController.view.frame = pageViewRect;

Step 6:Assign the gestureRecognizers property of our pageViewController to our view’s gestureRecognizers property. This will enable touch gestures to transition between the pages:

View Code OBJC
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;

Now, if we run the application, we can see our view controller with UIPageViewController Example first pageBut, if we swipe left to right, the views aren’t changing i.e. the gesture recognizers aren’t working.

So to make that happen, we have to conform our class to the UIPageViewControllerDataSource protocol and implement the following two methods:

(1) - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController

viewControllerBeforeViewController:(UIViewController *)viewController

(2) - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController

viewControllerAfterViewController:(UIViewController *)viewController

We write this code in our ViewController.m file:

View Code OBJC
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
      viewControllerBeforeViewController:(UIViewController *)viewController
{
    NSUInteger currentIndex = [self.modelArray indexOfObject:[(ContentViewController *)viewController labelContents]];
    if(currentIndex == 0)
    {
        return nil;
    }
    ContentViewController *contentViewController = [[ContentViewController alloc] init];
    contentViewController.labelContents = [self.modelArray objectAtIndex:currentIndex - 1];
    return contentViewController;
}
 
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
       viewControllerAfterViewController:(UIViewController *)viewController
{
    NSUInteger currentIndex = [self.modelArray indexOfObject:[(ContentViewController *)viewController labelContents]];
    if(currentIndex == self.modelArray.count-1)
    {
        return nil;
    }
    ContentViewController *contentViewController = [[ContentViewController alloc] init];
    contentViewController.labelContents = [self.modelArray objectAtIndex:currentIndex + 1];
    return contentViewController;
}

The above two methods will be called every time the user swipes left or right, and so we have to return the suitable view controller that will be presented. For demo purposes, here we just create an instance of our ContentViewController, see the currentIndex of the contents, and set the labelContents appropriately.

In a real life scenario, this part would differ, and instead of allocating a new instance of our ContentViewController, we could have a better logic, like having an array of say 3 controllers, and reusing them. But to avoid complexity, and with the intent of just demonstrating on how the UIPageViewController works, I have just allocated a new instance every time.

I we run the application now, we see that the transition is now happening between the pages!

But if we rotate to landscape orientation, we don’t see a partition, right. For that, we have to implement the UIPageViewControllerDelegate method:

- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation

and return the UIPageViewControllerSpineLocation according to the interface orientation.

The spine is actually a kind of binding we see in a notebook. It can be at three positions:

(a) UIPageViewControllerSpineLocationMin – The binding is on the left side.

(b) UIPageViewControllerSpineLocationMid – The binding is on the mid. This would be foe Landscape orientations, with like pages on both sides

and (c) UIPageViewControllerSpineLocationMax – The binding is on the right side.

We just write this code in our ViewController implementation:

View Code OBJC
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
                   spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    if(UIInterfaceOrientationIsPortrait(orientation))
    {
        // In portrait orientation: Set the spine position to "min" and the page view controller's view controllers array to contain just one view controller. Setting the spine position to 'UIPageViewControllerSpineLocationMid' in landscape orientation sets the doubleSided property to YES, so set it to NO here.
        UIViewController *currentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
        NSArray *viewControllers = [NSArray arrayWithObject:currentViewController];
        [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
 
        self.pageViewController.doubleSided = NO;
 
        return UIPageViewControllerSpineLocationMin;
    }
    else
    {
        // In landscape orientation: Set set the spine location to "mid" and the page view controller's view controllers array to contain two view controllers. If the current page is even, set it to contain the current and next view controllers; if it is odd, set the array to contain the previous and current view controllers.
        NSArray *viewControllers = nil;
        ContentViewController *currentViewController = [self.pageViewController.viewControllers objectAtIndex:0];
 
        NSUInteger currentIndex = [self.modelArray indexOfObject:[(ContentViewController *)currentViewController labelContents]];
        if(currentIndex == 0 || currentIndex %2 == 0)
        {
            UIViewController *nextViewController = [self pageViewController:self.pageViewController viewControllerAfterViewController:currentViewController];
            viewControllers = [NSArray arrayWithObjects:currentViewController, nextViewController, nil];
        }
        else
        {
            UIViewController *previousViewController = [self pageViewController:self.pageViewController viewControllerBeforeViewController:currentViewController];
            viewControllers = [NSArray arrayWithObjects:previousViewController, currentViewController, nil];
        }
        //Now, set the viewControllers property of UIPageViewController
        [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
 
        return UIPageViewControllerSpineLocationMid;
    }
}

and Bingo! When we run the app, in landscape orientation, we see a partition.

So lets scrutinize what we did in the delegate method.

We first set the viewControllers property of our pageViewController according to the interface orientation. We return an array of just one viewController if the orientation is portrait. In case of landscape, we return an array of two view controllers. If the current page is even, we set the array to contain the current and next view controllers; if it is odd, then we set the array to contain the previous and current view controllers.

We then set the spine location appropriately. We set it as UIPageViewControllerSpineLocationMin  in portrait and UIPageViewControllerSpineLocationMid in landscape.

An important thing to note: When we set the spine location to UIPageViewControllerSpineLocationMid,the doubleSided property of the pageViewController is automatically set to YES. This means that the content on page front will not partially show through back. But when this property is set to NO, the content on page front will partially show through back, giving the page a translucent kind of effect. So, in the portrait orientation, we have to set the value to NO, otherwise it would result in an exception.

We can also zoom in and out in a UIPageViewController. Please comment and let me know if you need a tutorial on that.

As usual, you can download all the sample code of this tutorial here:

UIPageViewControllerDemo

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics