47

Now it's white dots with black background. What about if I want it to be black dots with white backgrounds?

- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController NS_AVAILABLE_IOS(6_0)
{
    return _imageArrays.count;
}// The number of items reflected in the page indicator.
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController NS_AVAILABLE_IOS(6_0)
{
    return self.intCurrentIndex;
}// The selected item reflected in the page indicator.
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Septiadi Agus
  • 1,775
  • 3
  • 17
  • 26
  • 6
    The lack of customization hooks for the UIPageViewController's UIPageControl is a real problem. Apparently, you can't reposition it or change the colors. You also can't tell it to hide if there's only one page. In fact, UIPageViewController is a pretty lousy class all around — doesn't play nice with autolayout, creates all kind of magic subviews, etc. Unfortunately, pagination with UIScrollView is also problematic, since it opens the door for memory management issues when you've got lots of pages. – sirvine Jul 09 '13 at 01:49
  • http://www.raywenderlich.com/21703/user-interface-customization-in-ios-6 – yazh Jan 23 '15 at 13:00
  • I have a solution to this in Swift 4. Please check my answer below. – wsgeorge Jun 02 '18 at 13:11

11 Answers11

131

You can use UIAppearance to change the color of UIPageControl. Try this in your AppDelegate's didFinishLaunchingWithOptions function.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UIPageControl *pageControl = [UIPageControl appearance];
    pageControl.pageIndicatorTintColor = [UIColor lightGrayColor];
    pageControl.currentPageIndicatorTintColor = [UIColor blackColor];
    pageControl.backgroundColor = [UIColor blueColor];

    return YES;
}

EDIT:

To apply style only to a particular view controller, use appearanceWhenContainedIn instead, as following:

UIPageControl *pageControl = [UIPageControl appearanceWhenContainedIn:[MyViewController class], nil];

Now, only UIPageControl objects contained in the MyViewController are going to adapt this style.

Thanks Mike & Shingoo!

EDIT: If you see black background around UIPageControl at the bottom of your screen, it is due to the background color of your UIPageViewController not UIPageControl. You can change this color as following:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor blueColor]; //Set it to whatever you like
}
Yas Tabasam
  • 10,517
  • 9
  • 48
  • 53
  • 12
    You can go further and use `[UIPageControl appearenceInContainer:[MyViewController class], nil]` if want to avoid changing page controls globally – Mike Abdullah Oct 01 '13 at 10:22
  • 3
    [UIPageControl appearanceWhenContainedIn:[MyViewController class], nil] should be correct – Shingoo Oct 13 '13 at 11:00
  • 2
    I like this answer. Much more concise. – Kyle Clegg Oct 16 '13 at 01:50
  • Instead of App delegate you can add it in your PageViewControllers super controller. But +1 for this, much better solution than others I found. – GoodSp33d Oct 04 '14 at 08:49
  • @Yas I think the background color of PageControl is due to backGroundColor of view controller cause I have imageViews in pageviewController and the PageControl is present above those image and setting the backGroundColor property does not change the PageControl color. – meteors Apr 23 '15 at 06:36
  • Thanks for the answer. It's true that the black color is the background of the UIPageViewController. However, I can't set the color to clear color. If I set it to clear color, the background turns black. Please tell me if there is another way to achieve it by using tint or alpha or something.... – oky_sabeni May 06 '15 at 23:42
  • Can we change the shape of pagecontrol as well ? – Priyal Dec 27 '16 at 10:19
35

I don't believe that you can manipulate the UIPageViewController's page control. My solution:

I have a "root" UIViewController that is UIPageViewControllerDelegate and UIPageViewControllerDataSource.

On this root view controller, I have @property (strong, nonatomic) IBOutlet UIPageControl *pageControl. In the corresponding storyboard nib, I add a UIPageControl, position it, and check "Hides for Single Page". I can also change the colors, if I wish.

Then, I add the following in the root view controller's viewDidLoad: self.pageControl.numberOfPages = [self.features count]

My root view controller also has @property (strong, nonatomic) UIPageViewController *pageViewController. And in the implementation:

self.pageViewController = [[UIPageViewController alloc]
          initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll 
            navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal 
                          options:nil];


self.pageViewController.delegate = self;
DataViewController *startingViewController = [self viewControllerAtIndex:0 storyboard:self.storyboard];
NSArray *viewControllers = @[startingViewController];
[self.pageViewController setViewControllers:viewControllers
                                  direction:UIPageViewControllerNavigationDirectionForward
                                   animated:NO
                                 completion:NULL];
self.pageViewController.dataSource = self;      
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
self.pageViewController.view.frame = CGRectMake(0.0, 0.0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height + 10.0);
[self.pageViewController didMoveToParentViewController:self];
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;

(SIDE NOTE: That line that sets the frame makes the height of the UIPageViewController's view exceed the screen size so that the native page control is no longer visible. My app is portrait only, iPhone only, so I got off a bit easy here. If you need to handle rotations, you'll have to find a way to keep that native page control offscreen. I tried using auto layout, but UIPageViewController creates a set of magic views that have a bunch of autolayout mask constraints that I couldn't find a way to override.)

Anyway...then I add an extra UIPageViewController delegate method to change my new, non-native UIPageControl to the currently-selected page:

- (void)pageViewController:(UIPageViewController *)viewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
  if (!completed){return;}

  // Find index of current page
  DataViewController *currentViewController = (DataViewController *)[self.pageViewController.viewControllers lastObject];
  NSUInteger indexOfCurrentPage = [self indexOfViewController:currentViewController];
  self.pageControl.currentPage = indexOfCurrentPage;
}

Not as pretty as I would like, but Apple's API for this class doesn't exactly lend itself to elegance.

sirvine
  • 1,045
  • 11
  • 12
  • 5
    There's no need to resize the view frame. If you don't implement the data source page indicator methods the native page control doesn't appear and the extra margin at the bottom of the view isn't added. – Paul Linsay Jul 28 '15 at 23:01
  • Thank you very much. This page view controller solved my problem. For my case, I don't need the self.view.gestureRecognizers = self.pageViewController.gestureRecognizers; And also, I set the pageControl.userInteractionEnabled = NO; so when we swipe in the pageControl it wont update the currentPage attribute but the pageViewController is not changing the view controller. – felixwcf Dec 12 '16 at 15:45
7

You can actually grab it and store it locally in your own property in one of the delegate calls.

Put this code inside your delegate to access the UIPageControl inside the UIPageViewController:

- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
    [self setupPageControlAppearance];
    return kPageCount;
}

- (void)setupPageControlAppearance
{
    UIPageControl * pageControl = [[self.view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(class = %@)", [UIPageControl class]]] lastObject];
    pageControl.pageIndicatorTintColor = [UIColor grayColor];
    pageControl.currentPageIndicatorTintColor = [UIColor blackColor];
}
Yariv Nissim
  • 13,273
  • 1
  • 38
  • 44
4

You can recursively search it in your subviews

- (void)findAndConfigurePageControlInView:(UIView *)view
{
    for (UIView *subview in view.subviews) {
        if ([subview isKindOfClass:[UIPageControl class]]) {
            UIPageControl * pageControl = (UIPageControl *)subview;
            //customize here
            pageControl.hidesForSinglePage = YES;
            break;
        } else {
            [self findAndConfigurePageControlInView:subview];
        }
    }
}

- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
    [self findAndConfigurePageControlInView:self.view];
    return self.promotionsVCs.count;
}

it works for me

CyberKid
  • 49
  • 2
  • I tried something similar but I never got round to finding a subview that was a UIPageControl. Please see my answer below for what I did. – wsgeorge Jun 02 '18 at 13:12
3

Here's what I did in Swift 4. I tried similar answers first, in viewDidLoad, but this is what eventually worked. This same snippet was used to answer similar SO questions.

    override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    for view in self.view.subviews{
        if view is UIPageControl{
            (view as! UIPageControl).currentPageIndicatorTintColor = .yellow
            }
        }
    }

Once you have the UIPageControl in this block, you should be able to customize its indicator colours

wsgeorge
  • 1,928
  • 17
  • 20
2
UIPageControl *pageControl = [UIPageControl appearanceWhenContainedIn:[MyViewController class], nil];
pageControl.pageIndicatorTintColor = [UIColor whiteColor];
pageControl.currentPageIndicatorTintColor = [UIColor redColor];
pageControl.backgroundColor = [UIColor blackColor];

This will change the appearance just for "MyViewController". If you want to have different colors in different page indicators on the same view you have to create different subviews and customize them individually.

Shingoo
  • 826
  • 10
  • 26
1

If you use the "auto generated" page indicator created by UIPageViewController, I think that you can't customize it. The only way you could do that is to add an extra PageControl, either the one provided by Apple or a custom one as @Maschel proposed.

Heckscheibe
  • 630
  • 7
  • 8
1

It is possible to customise it through appearance. You can do it in AppDelegate like this.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   UIPageControl *pageControl = [UIPageControl appearance];
   pageControl.pageIndicatorTintColor = [UIColor whiteColor];
   pageControl.currentPageIndicatorTintColor = [UIColor blackColor];
   pageControl.backgroundColor = [UIColor lightGrayColor];

   return YES;
}

If you want to do it just for a certain view controller, replace the pageControl with this instead.

UIPageControl *pageControl = [UIPageControl appearanceWhenContainedIn:[MyViewController class], nil];
Zeezer
  • 1,503
  • 2
  • 18
  • 33
1

This one working perfectly for custom image

self.pageControl.pageIndicatorTintColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"page_indicater"]];

self.pageControl.currentPageIndicatorTintColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"page_indicater_selection"]];
Gurumoorthy Arumugam
  • 2,129
  • 1
  • 25
  • 40
0

You can use SMPageControl: Github. It works just like the UIPageControl but with more customisation possibilities.

kelin
  • 11,323
  • 6
  • 67
  • 104
Maschel
  • 137
  • 4
0

You can easily access the UIPageViewController's pageControl by defining a computed property like this:

var pageControl: UIPageControl? {
    for subview in view.subviews {
        if let pageControl = subview as? UIPageControl {
            return pageControl
        }
    }
    return nil
}

And then customize it to suite your needs like this:

override func viewDidLoad() {
    super.viewDidLoad()
    pageControl?.backgroundColor = .white
    pageControl?.pageIndicatorTintColor = .red
    pageControl?.currentPageIndicatorTintColor = .blue
}

Obvious caveat: if Apple ever decides to change the UIPageViewController view hierarchy this will stop working.

boliva
  • 5,604
  • 6
  • 37
  • 39