Dec 2 2009

Customisable UIPageControl

PageControl Examples

PageControl Examples


The UIPageControl has no way of changing the colors of the dots. Kind of annoying right? Well I knocked up a simple solution to customising the look of the UIPageControl.

This class allows you to replace the dots with your own UIImages, with the following 2 properties:

@property (nonatomic, readwrite, retain) UIImage* imageNormal;
@property (nonatomic, readwrite, retain) UIImage* imageCurrent;

It works by replacing the UIImage on the UIImageView containing the original dot.

One word of warning, if Apple change the way the UIPageControl works, this code will most likely break (especially if they change the dots so they are no longer UIImageViews!). However I’d say the only reason this would happen, is if they add their own functionality in to customise the look of it, so it’s probably OK.

Here’s the full code, in all it’s glory. Feel free to use how you see fit (I’m releasing this with no restrictions). It’s not very complicated, but I figured it might be useful to someone!

#import <UIKit/UIKit.h>
 
@interface OMPageControl : UIPageControl {
  UIImage* mImageNormal;
  UIImage* mImageCurrent;
}
 
@property (nonatomic, readwrite, retain) UIImage* imageNormal;
@property (nonatomic, readwrite, retain) UIImage* imageCurrent;
 
@end
#import "OMPageControl.h"
 
@interface OMPageControl (Private)
- (void) updateDots;
@end
 
 
@implementation OMPageControl
 
@synthesize imageNormal = mImageNormal;
@synthesize imageCurrent = mImageCurrent;
 
- (void) dealloc
{
  [mImageNormal release], mImageNormal = nil;
  [mImageCurrent release], mImageCurrent = nil;
 
	[super dealloc];
}
 
 
/** override to update dots */
- (void) setCurrentPage:(NSInteger)currentPage
{
  [super setCurrentPage:currentPage];
 
  // update dot views
  [self updateDots];
}
 
/** override to update dots */
- (void) updateCurrentPageDisplay
{
  [super updateCurrentPageDisplay];
 
  // update dot views
  [self updateDots];
}
 
/** Override setImageNormal */
- (void) setImageNormal:(UIImage*)image
{
  [mImageNormal release];
  mImageNormal = [image retain];
 
  // update dot views
  [self updateDots];
}
 
/** Override setImageCurrent */
- (void) setImageCurrent:(UIImage*)image
{
  [mImageCurrent release];
  mImageCurrent = [image retain];
 
  // update dot views
  [self updateDots];
}
 
/** Override to fix when dots are directly clicked */
- (void) endTrackingWithTouch:(UITouch*)touch withEvent:(UIEvent*)event 
{
  [super endTrackingWithTouch:touch withEvent:event];
 
  [self updateDots];
}
 
#pragma mark - (Private)
 
- (void) updateDots
{
  if(mImageCurrent || mImageNormal)
  {
    // Get subviews
    NSArray* dotViews = self.subviews;
    for(int i = 0; i < dotViews.count; ++i)
    {
      UIImageView* dot = [dotViews objectAtIndex:i];
      // Set image
      dot.image = (i == self.currentPage) ? mImageCurrent : mImageNormal;
    }
  }
}
 
@end

  • João Moreno Says:

    This is awesome, I’m going to try it out.

    Thanks!

  • Adam Says:

    Where do the dot images go?

  • Gunnar Says:

    That saved me some work and helped my understanding of using derived classes in Interface Builder. The Dots go anywhere in the Program directory, just like any other image.

  • Mostafa Amer Says:

    Hi,

    Thank you very much for this brilliant idea, it work like charm :)
    Some points to mention:
    1- It will start with standard white buttons and stay like this until something happen.
    To work around it, I overrided -(void)awakeFromNib; and called [self updateDots] inside it.

    2- Crashing the app is not cool :) I suggest checking if dot will respond to setImage: before trying to change it. Anyway white dots is better than crashing app ;)

    THX for the awesome code.

  • mg Says:

    where do you say which images have to be loaded from the filedirectory? do they have a standard name?

  • mg Says:

    where to i load the dot images? no clue what mImageNormal and mImageCurrent is. i have to images one is dark grey and on is dark black. They are supposed to replace the common dot images of the UIPageControl. But where to i set the image?

    any hints?

  • Oded Says:

    This seems like using Apple’s private APIs. The methods you are overriding are not documented in the headers.

    Doesn’t this risk my app not being approved at the App Store?

    thanks for the cool idea though! :)

  • Joel Teply Says:

    Had to add the following to make work in 3.0 and get the page flipping fixed in some cases. Thanks by the way…

    @dynamic numberOfPages;
    @dynamic currentPage;

    - (void) setCurrentPage:(NSInteger)currentPage {
    [super setCurrentPage:currentPage];
    [self updateDots];
    }

  • Amy Says:

    This seems like using Apple’s private APIs. The methods you are overriding are not documented in the headers.

    Doesn’t this risk my app not being approved at the App Store?

    thanks for the cool idea though! :)

  • Tweets that mention Customisable UIPageControl | OniDev -- Topsy.com Says:

    [...] This post was mentioned on Twitter by Ryoichi Tanaka. Ryoichi Tanaka said: UIPageControlのカスタマイズ簡単だった。ありがたやー。 http://j.mp/95h0vk [...]

  • cpalead Says:

    I’ve been searching this for a long time, thanks for sharing.

  • Greg Maletic Says:

    Nice work. (Though I’m slightly concerned about using private APIs to accomplish this.)

    One problem I did notice: if you tap on the page control, the dots revert to their standard white/gray appearance.

  • Bruce Says:

    Where do the dot images go?

  • Mitch Says:

    This is not using any private APIs.

  • Steve Says:

    Where do the dot images go?

  • Ehssan Says:

    Thanx man…this works like a charm :)

  • Steve Says:

    My solution to the problem Greg Maletic found -where the page controllers revert back to white/grey after the page control is directly clicked- is to call setImageNormal and setImageCurrent from within function that gets invoked. It’s hacky, but it works.

    [pgControl addTarget:self action:@selector(pageControlDidChange) forControlEvents:UIControlEventValueChanged];

    Then, inside pageControlDidChange, in addition to the normal behavior you may be looking for, call the setImageNormal and setImageCurrent again

  • Steve Says:

    OMG Ignore what I said above. Unless you like making hacky code. Instead your answer lies in overriding one more method.

    Put this in your implementation.

    - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    [super endTrackingWithTouch:touch withEvent:event];
    [self updateDots];
    }

  • Tojas Says:

    This code is very good.
    Also works on iOS 4.0, but you have to use Steve’s last comment. Thanks guys!

  • Paulo Says:

    How do I increase the size of dots?

  • Paulo Says:

    You can change the size doin’g this:
    - (void)updateDots {

    if (imgCurrent || imgNormal) {
    NSArray *dotViews = self.subviews;
    for (int i = 0; i < dotViews.count; ++i) {
    UIImageView *dot = [dotViews objectAtIndex:i];
    dot.image = (i == self.currentPage) ? imgCurrent : imgNormal;
    dot.frame = CGRectMake(0, 0, imgNormal.size.width, imgNormal.size.height);
    }
    }
    }

  • Mitch Says:

    A bit late I know! But I’ve just updated the original code in the post to include the fix from Steve. Thanks.

Leave a Reply