Integrating UIPageViewController and UISegmentedControl in iOS for Seamless Navigation Experience

Understanding UIPageViewController and UISegmentedControl in iOS

UIPageViewController is a powerful view controller class in iOS that allows you to implement a paging interface for your views. It’s commonly used in applications with large datasets or many pages of content, where the user needs to navigate between them. However, integrating it with a UISegmentedControl (also known as a segmented control) can be tricky.

A UISegmentedControl is a simple UI element that consists of one or more segments, which are horizontal bars that represent different options. It’s often used in applications to provide users with multiple options and allow them to select one by touching the corresponding segment. In this article, we’ll explore how to integrate a UIPageViewController with a UISegmentedControl in iOS.

Setting Up the Scene

To demonstrate the integration of UIPageViewController and UISegmentedControl, let’s create a basic example. We’ll start by setting up a UIPageViewController instance and a UISegmentedControl:

#import <UIKit/UIKit.h>

@interface MasterTableViewController : UIViewController

@end

@implementation MasterTableViewController

- (instancetype)init {
    self = [super init];
    if (self) {
        // Initialize the segmented control
        _segmentedControl = [[UISegmentedControl alloc] initWithItems:@[@"On Campus", @"Off Campus", @"My Events"]];
        
        // Configure the page view controller
        _pageViewController = [[UIPageViewController alloc] initWithViewControllers:@[] configuration:nil];
    }
    return self;
}

@end

Integrating UIPageViewController and UISegmentedControl

Now that we have our basic setup, let’s integrate the UIPageViewController with the UISegmentedControl. We’ll start by implementing the willTransitionToViewControllers: method in our view controller:

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
    if ([pendingViewControllers count] > 0) {
        NSUInteger index = [(MasterTableViewController *)[pendingViewControllers objectAtIndex:0] pageIndex];
        _startingViewController.pageIndex = index;
    }
}

In this method, we’re updating the _startingViewController instance variable with the current page index. We’ll also implement the didFinishAnimating: method to update the segmented control:

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
    if (finished && completed && [previousViewControllers count] > 0) {
        NSUInteger index = [(MasterTableViewController *)[previousViewControllers objectAtIndex:0] pageIndex];
        _segmentedControl.selectedSegmentIndex = _startingViewController.pageIndex;
        self.lastSelected = index;
    }
}

In this method, we’re updating the segmented control’s selected segment index with the current page index. We’ll also update our _lastSelected instance variable to keep track of the last selected page.

Understanding Page Indexing

Before we move on, let’s understand how page indexing works in UIPageViewController. When you add a view controller to your page view controller, it will be added at the current page index. If the new view controller has a different page index, it will overwrite the current page index.

To implement this logic in our example, we’ve created two methods: pageViewController:willTransitionToViewControllers: and pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:. These methods update the _startingViewController instance variable with the current page index when a transition occurs.

Implementing Page Indexing Logic

Now that we have our basic setup, let’s implement the page indexing logic in more detail. We’ll start by creating an MasterTableViewController subclass:

#import <UIKit/UIKit.h>

@interface MasterTableViewController : UIViewController

@property (nonatomic, strong) UISegmentedControl *segmentedControl;
@property (nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, assign) NSUInteger pageIndex;

@end

@implementation MasterTableViewController

- (instancetype)init {
    self = [super init];
    if (self) {
        // Initialize the segmented control
        _segmentedControl = [[UISegmentedControl alloc] initWithItems:@[@"On Campus", @"Off Campus", @"My Events"]];
        
        // Configure the page view controller
        _pageViewController = [[UIPageViewController alloc] initWithViewControllers:@[] configuration:nil];
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Add the segmented control to the view controller's view
    [_segmentedControl sizeToFit];
    [_segmentedControl addTarget:self action:@selector(segmentedControlChanged:) forControlEvents:UIControlEventValueChanged];
    
    // Configure the page view controller
    _pageViewController.delegate = self;
}

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
    if ([pendingViewControllers count] > 0) {
        NSUInteger index = [(MasterTableViewController *)[pendingViewControllers objectAtIndex:0] pageIndex];
        _pageIndex = index;
        
        // Update the segmented control's selected segment index
        [_segmentedControl setSelectedSegmentIndex:_pageIndex];
    }
}

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
    if (finished && completed && [previousViewControllers count] > 0) {
        NSUInteger index = [(MasterTableViewController *)[previousViewControllers objectAtIndex:0] pageIndex];
        _pageIndex = index;
        
        // Update the segmented control's selected segment index
        [_segmentedControl setSelectedSegmentIndex:_pageIndex];
    }
}

- (void)segmentedControlChanged:(UISegmentedControl *)sender {
    NSUInteger index = [sender indexOfSelectedSegment];
    
    // Create a new view controller for the selected page
    UIViewController *viewController = [self viewControllerAtIndex:index];
    
    if (viewController != nil) {
        // Set the page view controller's view controllers array
        [_pageViewController setViewControllers:@[viewController] direction:UIPageViewControllerDirectionForward orientation:UIPageViewControllerOrientationVertical size:1.0 animated:YES];
        
        // Reset the page index
        _pageIndex = index;
    }
}

- (UIViewController *)viewControllerAtIndex:(NSUInteger)index {
    if (([self.pageTitles count] == 0) || (index >= [self.pageTitles count])) {
        return nil;
    }
    
    MasterTableViewController *pageContentViewController;
    
    if (index == 0)
    {
        pageContentViewController = [storyboard instantiateViewControllerWithIdentifier:@"OnCampusTable"];
    }
    else if (index == 1)
    {
        pageContentViewController = [storyboard instantiateViewControllerWithIdentifier:@"OffCampusTable"];
    }
    else if (index == 2)
    {
        pageContentViewController = [storyboard instantiateViewControllerWithIdentifier:@"MyEventsTable"];
    }
    
    return pageContentViewController;
}

@end

In this updated implementation, we’ve added a pageIndex property to our view controller. We’ve also implemented the willTransitionToViewControllers: and didFinishAnimating:previousViewControllers:transitionCompleted: methods to update the _pageIndex instance variable with the current page index.

Conclusion

Integrating UIPageViewController and UISegmentedControl in iOS can be tricky, but by understanding how these view controllers work together, you can create a seamless experience for your users. By implementing both willTransitionToViewControllers: and didFinishAnimating:previousViewControllers:transitionCompleted:, we’ve been able to solve the issue of the segmented control being lagged behind one move.

We hope this article has provided you with a better understanding of how UIPageViewController and UISegmentedControl integrate in iOS.


Last modified on 2023-06-08