193

I am developing an application in which I want to change either color or image of UIPageControl pagination dots. How can I change it? Is it possible to customize UIpageControl on above scenario?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Tirth
  • 7,801
  • 9
  • 55
  • 88

17 Answers17

279

UPDATE:

This answer is 6 years old and very outdated, but it's still attracting votes and comments. Ever since iOS 6.0 you should be using the pageIndicatorTintColor and currentPageIndicatorTintColor properties on UIPageControl.

ORIGINAL ANSWER:

I ran into this problem today and decided to write my own simple replacement class.

It's a sublassed UIView that uses Core Graphics to render the dots in the colors you specify.

You use the exposed properties to customize and control it.

If you want to you can register a delegate object to get notifications when the user taps on one of the little page dots. If no delegate is registered then the view will not react to touch input.

It's completely fresh from the oven, but seems to work. Let me know if you run into any problems with it.

Future improvements:

  • Resize the dots to fit the current bounds if there are too many.
  • Don't redraw the entire view in drawRect:

Example use:

CGRect f = CGRectMake(0, 0, 320, 20); 
PageControl *pageControl = [[[PageControl alloc] initWithFrame:f] autorelease];
pageControl.numberOfPages = 10;
pageControl.currentPage = 5;
pageControl.delegate = self;
[self addSubview:pageControl];

Header file:

//
//  PageControl.h
//
//  Replacement for UIPageControl because that one only supports white dots.
//
//  Created by Morten Heiberg <morten@heiberg.net> on November 1, 2010.
//

#import <UIKit/UIKit.h>

@protocol PageControlDelegate;

@interface PageControl : UIView 
{
@private
    NSInteger _currentPage;
    NSInteger _numberOfPages;
    UIColor *dotColorCurrentPage;
    UIColor *dotColorOtherPage;
    NSObject<PageControlDelegate> *delegate;
    //If ARC use __unsafe_unretained id delegate;
}

// Set these to control the PageControl.
@property (nonatomic) NSInteger currentPage;
@property (nonatomic) NSInteger numberOfPages;

// Customize these as well as the backgroundColor property.
@property (nonatomic, retain) UIColor *dotColorCurrentPage;
@property (nonatomic, retain) UIColor *dotColorOtherPage;

// Optional delegate for callbacks when user taps a page dot.
@property (nonatomic, retain) NSObject<PageControlDelegate> *delegate;

@end

@protocol PageControlDelegate<NSObject>
@optional
- (void)pageControlPageDidChange:(PageControl *)pageControl;
@end

Implementation file:

//
//  PageControl.m
//
//  Replacement for UIPageControl because that one only supports white dots.
//
//  Created by Morten Heiberg <morten@heiberg.net> on November 1, 2010.
//

#import "PageControl.h"

// Tweak these or make them dynamic.
#define kDotDiameter 7.0
#define kDotSpacer 7.0

@implementation PageControl

@synthesize dotColorCurrentPage;
@synthesize dotColorOtherPage;
@synthesize delegate;

- (NSInteger)currentPage
{
    return _currentPage;
}

- (void)setCurrentPage:(NSInteger)page
{
    _currentPage = MIN(MAX(0, page), _numberOfPages-1);
    [self setNeedsDisplay];
}

- (NSInteger)numberOfPages
{
    return _numberOfPages;
}

- (void)setNumberOfPages:(NSInteger)pages
{
    _numberOfPages = MAX(0, pages);
    _currentPage = MIN(MAX(0, _currentPage), _numberOfPages-1);
    [self setNeedsDisplay];
}

    - (id)initWithFrame:(CGRect)frame
{
    if ((self = [super initWithFrame:frame]))
    {
        // Default colors.
        self.backgroundColor = [UIColor clearColor];
        self.dotColorCurrentPage = [UIColor blackColor];
        self.dotColorOtherPage = [UIColor lightGrayColor];

        UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipedRight:)];
        [swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
        [self addGestureRecognizer:swipeRight];




        UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipedLeft:)];
        [swipe setDirection:UISwipeGestureRecognizerDirectionLeft];
        [self addGestureRecognizer:swipe];

    }
    return self;
}
-(void) swipedLeft:(UISwipeGestureRecognizer *) recognizer
{
    self.currentPage++;
}
-(void) swipedRight:(UISwipeGestureRecognizer *) recognizer
{
    self.currentPage--;
}

- (void)drawRect:(CGRect)rect 
{
    CGContextRef context = UIGraphicsGetCurrentContext();   
    CGContextSetAllowsAntialiasing(context, true);

    CGRect currentBounds = self.bounds;
    CGFloat dotsWidth = self.numberOfPages*kDotDiameter + MAX(0, self.numberOfPages-1)*kDotSpacer;
    CGFloat x = CGRectGetMidX(currentBounds)-dotsWidth/2;
    CGFloat y = CGRectGetMidY(currentBounds)-kDotDiameter/2;
    for (int i=0; i<_numberOfPages; i++)
    {
        CGRect circleRect = CGRectMake(x, y, kDotDiameter, kDotDiameter);
        if (i == _currentPage)
        {
            CGContextSetFillColorWithColor(context, self.dotColorCurrentPage.CGColor);
        }
        else
        {
            CGContextSetFillColorWithColor(context, self.dotColorOtherPage.CGColor);
        }
        CGContextFillEllipseInRect(context, circleRect);
        x += kDotDiameter + kDotSpacer;
    }
}

- (void)dealloc 
{
    [dotColorCurrentPage release];
    [dotColorOtherPage release];
    [delegate release];
    [super dealloc];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (!self.delegate) return;

    CGPoint touchPoint = [[[event touchesForView:self] anyObject] locationInView:self];

    CGFloat dotSpanX = self.numberOfPages*(kDotDiameter + kDotSpacer);
    CGFloat dotSpanY = kDotDiameter + kDotSpacer;

    CGRect currentBounds = self.bounds;
    CGFloat x = touchPoint.x + dotSpanX/2 - CGRectGetMidX(currentBounds);
    CGFloat y = touchPoint.y + dotSpanY/2 - CGRectGetMidY(currentBounds);

    if ((x<0) || (x>dotSpanX) || (y<0) || (y>dotSpanY)) return;

    self.currentPage = floor(x/(kDotDiameter+kDotSpacer));
    if ([self.delegate respondsToSelector:@selector(pageControlPageDidChange:)])
    {
        [self.delegate pageControlPageDidChange:self];
    }
}

@end
Heiberg
  • 5,507
  • 2
  • 26
  • 28
  • So how does this work? I'm using pagecontrolPageDidChange method and I'm not getting anything. I can't click any of the buttons – Adam Sep 05 '11 at 17:35
  • HI Heiberg, i used this to change my page of scrollview, how do you do it from your code ? [pageControl1 addTarget:self action:@selector(changePage:) forControlEvents:UIControlEventValueChanged]; – Desmond Nov 03 '11 at 12:01
  • //Action for page changing on UIPageControl -(void)changePage:(UIPageControl*)control { //int page = pageControl.currentPage; int page = pageControl.currentPage; // update the scroll view to the appropriate page CGRect frame = scrollview.frame; frame.origin.x = frame.size.width * page; frame.origin.y = 0; [scrollview scrollRectToVisible:frame animated:YES]; pageControlUsed = YES; } – Desmond Nov 03 '11 at 12:04
  • For running this code with ARC you'll simply need to remove the dealloc method, change assign to weak and add a __weak before the concerned property declaration. Very nice. Thanks a lot. – cschuff Dec 01 '11 at 16:17
  • replace NSObject *delegate with __unsafe_unretained id delegate; in header to resolve ARC warning – Mihir Mehta Aug 26 '13 at 13:59
  • @vibhor: These days you should be using the properties on UIPageControl that were introduced in iOS 6. – Heiberg Mar 03 '16 at 13:51
156

In iOS 6 you can set the tint color of UIPageControl:

There are 2 new properties:

  • pageIndicatorTintColor
  • currentPageIndicatorTintColor

You can also use the appearance API to change the tint color of all page indicators.

If you are targeting iOS 5 make sure it doesn't crash:

if ([pageControl respondsToSelector:@selector(setPageIndicatorTintColor:)]) {
    pageControl.pageIndicatorTintColor = [UIColor whiteColor];
}
Matthias Bauch
  • 89,811
  • 20
  • 225
  • 247
Felix
  • 35,354
  • 13
  • 96
  • 143
43
pageControl.pageIndicatorTintColor = [UIColor redColor];
pageControl.currentPageIndicatorTintColor = [UIColor redColor];

works for iOS6

user1349663
  • 595
  • 1
  • 7
  • 21
Add080bbA
  • 1,818
  • 1
  • 18
  • 25
23

In case anyone wants an ARC / modern version of it (no need to redefine properties as ivar, no dealloc, and works with Interface Builder) :

#import <UIKit/UIKit.h>

@protocol PageControlDelegate;

@interface PageControl : UIView 

// Set these to control the PageControl.
@property (nonatomic) NSInteger currentPage;
@property (nonatomic) NSInteger numberOfPages;

// Customize these as well as the backgroundColor property.
@property (nonatomic, strong) UIColor *dotColorCurrentPage;
@property (nonatomic, strong) UIColor *dotColorOtherPage;

// Optional delegate for callbacks when user taps a page dot.
@property (nonatomic, weak) NSObject<PageControlDelegate> *delegate;

@end

@protocol PageControlDelegate<NSObject>
@optional
- (void)pageControlPageDidChange:(PageControl *)pageControl;
@end

PageControl.m :

#import "PageControl.h"


// Tweak these or make them dynamic.
#define kDotDiameter 7.0
#define kDotSpacer 7.0

@implementation PageControl

@synthesize dotColorCurrentPage;
@synthesize dotColorOtherPage;
@synthesize currentPage;
@synthesize numberOfPages;
@synthesize delegate;

- (void)setCurrentPage:(NSInteger)page
{
    currentPage = MIN(MAX(0, page), self.numberOfPages-1);
    [self setNeedsDisplay];
}

- (void)setNumberOfPages:(NSInteger)pages
{
    numberOfPages = MAX(0, pages);
    currentPage = MIN(MAX(0, self.currentPage), numberOfPages-1);
    [self setNeedsDisplay];
}

- (id)initWithFrame:(CGRect)frame 
{
    if (self = [super initWithFrame:frame]) 
    {
        // Default colors.
        self.backgroundColor = [UIColor clearColor];
        self.dotColorCurrentPage = [UIColor blackColor];
        self.dotColorOtherPage = [UIColor lightGrayColor];
    }
    return self;
}

-(id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder])
    {
        self.dotColorCurrentPage = [UIColor blackColor];
        self.dotColorOtherPage = [UIColor lightGrayColor];
    }
    return self;
}

- (void)drawRect:(CGRect)rect 
{
    CGContextRef context = UIGraphicsGetCurrentContext();   
    CGContextSetAllowsAntialiasing(context, true);

    CGRect currentBounds = self.bounds;
    CGFloat dotsWidth = self.numberOfPages*kDotDiameter + MAX(0, self.numberOfPages-1)*kDotSpacer;
    CGFloat x = CGRectGetMidX(currentBounds)-dotsWidth/2;
    CGFloat y = CGRectGetMidY(currentBounds)-kDotDiameter/2;
    for (int i=0; i<self.numberOfPages; i++)
    {
        CGRect circleRect = CGRectMake(x, y, kDotDiameter, kDotDiameter);
        if (i == self.currentPage)
        {
            CGContextSetFillColorWithColor(context, self.dotColorCurrentPage.CGColor);
        }
        else
        {
            CGContextSetFillColorWithColor(context, self.dotColorOtherPage.CGColor);
        }
        CGContextFillEllipseInRect(context, circleRect);
        x += kDotDiameter + kDotSpacer;
    }
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (!self.delegate) return;

    CGPoint touchPoint = [[[event touchesForView:self] anyObject] locationInView:self];

    CGFloat dotSpanX = self.numberOfPages*(kDotDiameter + kDotSpacer);
    CGFloat dotSpanY = kDotDiameter + kDotSpacer;

    CGRect currentBounds = self.bounds;
    CGFloat x = touchPoint.x + dotSpanX/2 - CGRectGetMidX(currentBounds);
    CGFloat y = touchPoint.y + dotSpanY/2 - CGRectGetMidY(currentBounds);

    if ((x<0) || (x>dotSpanX) || (y<0) || (y>dotSpanY)) return;

    self.currentPage = floor(x/(kDotDiameter+kDotSpacer));
    if ([self.delegate respondsToSelector:@selector(pageControlPageDidChange:)])
    {
        [self.delegate pageControlPageDidChange:self];
    }
}

@end
Ben G
  • 3,965
  • 3
  • 30
  • 34
  • 1
    A minor addition to stop it sending to the delegate if the page number didn't actually change after a touch. NSInteger newPage = floor(x/(kDotDiameter+kDotSpacer)); if (self.currentPage == newPage) return; – theLastNightTrain Jul 16 '12 at 12:59
15

The answer provided by Heiberg works really well, however the page control does not behave exactly like the one by apple.

If you want the page control to behave like the one from apple does (always increment the current page by one if you touch the second half, otherwise decrease by one), try this touchesBegan-method instead:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

    CGPoint touchPoint = [[[event touchesForView:self] anyObject] locationInView:self];

    CGRect currentBounds = self.bounds;
    CGFloat x = touchPoint.x - CGRectGetMidX(currentBounds);

    if(x<0 && self.currentPage>=0){
        self.currentPage--;
        [self.delegate pageControlPageDidChange:self]; 
    }
    else if(x>0 && self.currentPage<self.numberOfPages-1){
        self.currentPage++;
        [self.delegate pageControlPageDidChange:self]; 
    }   
}
ChristophK
  • 3,157
  • 2
  • 25
  • 29
9

Add the following code to DidFinishLauch in AppDelegate,

UIPageControl *pageControl = [UIPageControl appearance];
pageControl.pageIndicatorTintColor = [UIColor lightGrayColor];
pageControl.currentPageIndicatorTintColor = [UIColor blackColor];
pageControl.backgroundColor = [UIColor whiteColor];

Hope this will help.

posha
  • 881
  • 4
  • 16
  • 28
9

In Swift, this code inside the UIPageViewController is getting a reference to the page indicator and setting its properties

override func viewDidLoad() {
    super.viewDidLoad()

    //Creating the proxy
    let pageControl = UIPageControl.appearance()
    //Customizing
    pageControl.pageIndicatorTintColor = UIColor.lightGrayColor()
    pageControl.currentPageIndicatorTintColor = UIColor.darkGrayColor()
    //Setting the background of the view controller so the dots wont be on a black background   
    self.view.backgroundColor = UIColor.whiteColor()
}
Pang
  • 9,564
  • 146
  • 81
  • 122
arbel03
  • 1,217
  • 2
  • 14
  • 24
7

use this for coding

if ([pageControl respondsToSelector:@selector(setPageIndicatorTintColor:)]) {
    pageControl.pageIndicatorTintColor = [UIColor whiteColor];
}

or from storyboard you can change from current page tint

enter image description here

Pooja Patel
  • 626
  • 8
  • 12
6

You can fix it with ease by adding the following code to your appdelegate.m file in your didFinishLaunchingWithOptions method:

UIPageControl *pageControl = [UIPageControl appearance];
pageControl.pageIndicatorTintColor = [UIColor darkGrayColor];
pageControl.currentPageIndicatorTintColor = [UIColor orangeColor];
pageControl.backgroundColor = [UIColor whiteColor]
honk
  • 9,137
  • 11
  • 75
  • 83
5

Adding to existing answers, it can be done like,

enter image description here

aToz
  • 2,463
  • 1
  • 24
  • 34
4

This is worked for me in iOS 7.

pageControl.pageIndicatorTintColor = [UIColor purpleColor];
pageControl.currentPageIndicatorTintColor = [UIColor magentaColor];
Sid
  • 137
  • 1
  • 1
4

It's easy with Swift 1.2:

UIPageControl.appearance().pageIndicatorTintColor           = UIColor.lightGrayColor()
UIPageControl.appearance().currentPageIndicatorTintColor    = UIColor.redColor()
josliber
  • 43,891
  • 12
  • 98
  • 133
Oleg Popov
  • 2,464
  • 1
  • 17
  • 15
  • 4
    This sets it globally. If you have multiple UIPageControls in your app and you need different colors based on class then use `UIPageControl.appearanceWhenContainedInInstancesOfClasses([MyClassName.self])` instead of `UIPageControl.appearance()`. Requires iOS 9. – Jon Jan 29 '16 at 17:10
3

In cased of Swift 2.0 and up, the below code will work:

pageControl.pageIndicatorTintColor = UIColor.whiteColor()
pageControl.currentPageIndicatorTintColor = UIColor.redColor()
Sohil R. Memon
  • 9,404
  • 1
  • 31
  • 57
2

@Jasarien I think you can subclass UIPageControll, line picked from apple doc only "Subclasses that customize the appearance of the page control can use this method to resize the page control when the page count changes" for the method sizeForNumberOfPages:

dsaw
  • 577
  • 5
  • 12
2

You could also use Three20 Library that contains a styleable PageControl and dozens of other helpful UI Controls and Abstractions.

cschuff
  • 5,502
  • 7
  • 36
  • 52
1

It's not possible using the iPhone SDK from an official standpoint. You might be able to do it using private methods, but that will be a barrier to getting onto the app store.

The only other safe solution is to create yout own page control which shpuldnt be too difficult given that the page control simply displays what page is currently shown in a scroll view.

Jasarien
  • 58,279
  • 31
  • 157
  • 188
  • Theere isn't a link to my solution. My solution is there in text just above your comment. Either look for the private methods (I won't know what these are) or write your own (I'm not going to do that for you). – Jasarien May 31 '10 at 13:41
-1
myView.superview.tintColor = [UIColor colorWithRed:1.0f  
                                      green:1.0f blue:1.0f alpha:1.0f];
kleopatra
  • 51,061
  • 28
  • 99
  • 211