iOS - PageViewController & SegmentedControl

In this article, we use SegmentedControl and PageViewController to change page.

Step 1: UI Design

Add Segmented Control in main page.

Add two ViewController and setup storyboard id : firstPage and secondPage.

Step 2: Reference SegmentedControl

Setup SegmentedControl object and click event in ViewController.swift.

Using UISegmentedControl.selectedSegmentIndex to get the current tab’s id.

Step 3: Setup PageViewController

Setup PageView Content

Add a PageViewController

1
var pageController: UIPageViewController!

Scroll view to change page.

1
pageController = UIPageViewController(transitionStyle: .Scroll, navigationOrientation:.Horizontal, options: nil)

Show the position of PageView.

1
pageController!.view.frame = CGRectMake(0, mPageSegment.frame.origin.y + mPageSegment.frame.height, view.frame.size.width, view.frame.size.height);

Setup main page.

1
2
currentPageIndex = 0;
pageController.setViewControllers([viewControllers.objectAtIndex(currentPageIndex) as! UIViewController], direction: .Forward, animated: false) {(isFinished:Bool) -> Void in}

Monitoring the changed event of UIPageViewControllerDelegate and UIPageViewControllerDataSource.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pageController.delegate = self;
pageController.dataSource = self;

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

return nil
}



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

return nil
}
}



extension ViewController: UIPageViewControllerDelegate {
func pageViewController(pageViewController: UIPageViewController,

}
}

Add the PageViewController.

1
2
self.addChildViewController(pageController)
self.view.addSubview(pageController.view)

Step 4: Setup content when changing page

When scrolling page from left to right, if the previous page index smaller than 0, return nil.

1
2
3
4
5
6
7
8
9
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
currentPageIndex = viewController.view.tag
let pageIndex = viewController.view.tag - 1;
if pageIndex < 0 {
return nil
}

return viewControllers[pageIndex] as? UIViewController
}

When scrolling page from right to left, if the net page index larger than total page, return nil.

1
2
3
4
5
6
7
8
9
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
currentPageIndex = viewController.view.tag
let pageIndex = viewController.view.tag + 1;
if pageIndex > 1 {
return nil
}

return viewControllers[pageIndex] as? UIViewController
}

Now we used Segmented Controll to control PageView.

Step 5: Change Segment

Using SegmentControll.selectedSegmentIndex to change selected tab.

In Step 4, add line : mPageSegment.selectedSegmentIndex = currentPageIndex.

Step 6: Click SegmentControll

When click SegmentControll many time, we will get the NSInternalInconsistencyException.

Because it have an animation when changing Pageview.
If the animation isn’t finish, we click SegmentControll again, it throw an NSInternalInconsistencyException.

Solutions:

  • Disable Animation

    1
    pageController!.setViewControllers([self.viewControllers[index] as! UIViewController], direction: .Reverse, animated: false, completion:nil)
  • After the animation finished

We using second method. Clicking SegmentedControll and setup the variable : enabled is false, when the animation finished, setup true.

Control SegmentedControll
Goto previous page.
direction: UIPageViewControllerNavigationDirection.Forward

Goto next page.
direction: UIPageViewControllerNavigationDirection.Reverse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@IBAction func tapSegment(sender: AnyObject) {
mPageSegment.enabled = false;

let index = (sender as! UISegmentedControl).selectedSegmentIndex;
if (currentPageIndex < index) {
pageController!.setViewControllers([self.viewControllers[index] as! UIViewController], direction: .Forward, animated: true, completion:{(isFinished: Bool) in
self.mPageSegment.enabled = true;
})
} else {
pageController!.setViewControllers([self.viewControllers[index] as! UIViewController], direction: .Reverse, animated: true, completion:{(isFinished: Bool) in
self.mPageSegment.enabled = true;
})
}

currentPageIndex = index
}

Using scroll method, when the animation is finished, set SegmentControll.enabled is true.

1
2
3
4
5
extension ViewController: UIPageViewControllerDelegate {
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
mPageSegment.enabled = true;
}
}

Source Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import UIKit

class ViewController: UIViewController {
@IBOutlet weak var mPageSegment: UISegmentedControl!
@IBAction func tapSegment(sender: AnyObject) {
mPageSegment.enabled = false;
let index = (sender as! UISegmentedControl).selectedSegmentIndex;

if (currentPageIndex < index) {
pageController!.setViewControllers([self.viewControllers[index] as! UIViewController], direction: .Forward, animated: true, completion:{(isFinished: Bool) in
self.mPageSegment.enabled = true;
})
} else {
pageController!.setViewControllers([self.viewControllers[index] as! UIViewController], direction: .Reverse, animated: true, completion:{(isFinished: Bool) in
self.mPageSegment.enabled = true;
})
}
currentPageIndex = index
}

var pageController: UIPageViewController!
var viewControllers = NSMutableArray()
var currentPageIndex : Int!

override func viewDidLoad() {
super.viewDidLoad()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let firstViewController = storyboard.instantiateViewControllerWithIdentifier("firstPage")
firstViewController.view.tag = 0;
viewControllers.addObject(firstViewController)

let secondViewController = storyboard.instantiateViewControllerWithIdentifier("secondPage")
secondViewController.view.tag = 1;
viewControllers.addObject(secondViewController)

pageController = UIPageViewController(transitionStyle: .Scroll, navigationOrientation:.Horizontal, options: nil)

pageController!.view.frame = CGRectMake(0, mPageSegment.frame.origin.y + mPageSegment.frame.height, view.frame.size.width, view.frame.size.height);

currentPageIndex = 0;
pageController.setViewControllers([viewControllers.objectAtIndex(currentPageIndex) as! UIViewController], direction: .Forward, animated: false) {(isFinished:Bool) -> Void in

}

pageController.delegate = self;
pageController.dataSource = self;

self.addChildViewController(pageController)
self.view.addSubview(pageController.view)
}

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

extension ViewController: UIPageViewControllerDataSource {
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
currentPageIndex = viewController.view.tag
mPageSegment.selectedSegmentIndex = currentPageIndex;
let pageIndex = viewController.view.tag - 1;
if pageIndex < 0 {
return nil
}
return viewControllers[pageIndex] as? UIViewController
}

func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
currentPageIndex = viewController.view.tag
mPageSegment.selectedSegmentIndex = currentPageIndex;
let pageIndex = viewController.view.tag + 1;
if pageIndex > 1 {
return nil
}
return viewControllers[pageIndex] as? UIViewController
}
}

extension ViewController: UIPageViewControllerDelegate {
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
mPageSegment.enabled = true;
}
}

Result



Author

Nick Lin

Posted on

2016-10-18

Updated on

2023-01-18

Licensed under


Comments