Dec
2
2009
Customisable UIPageControl

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
January 13th, 2010 at 10:13 pm
This is awesome, I’m going to try it out.
Thanks!
February 10th, 2010 at 11:01 am
Where do the dot images go?
March 18th, 2010 at 2:54 pm
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.
March 18th, 2010 at 7:13 pm
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.
March 19th, 2010 at 9:28 am
where do you say which images have to be loaded from the filedirectory? do they have a standard name?
March 23rd, 2010 at 4:19 pm
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?
March 26th, 2010 at 9:55 pm
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!
April 9th, 2010 at 10:15 pm
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];
}
April 23rd, 2010 at 12:48 pm
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!
April 28th, 2010 at 4:29 am
[...] This post was mentioned on Twitter by Ryoichi Tanaka. Ryoichi Tanaka said: UIPageControlのカスタマイズ簡単だった。ありがたやー。 http://j.mp/95h0vk [...]
May 13th, 2010 at 8:58 am
I’ve been searching this for a long time, thanks for sharing.
May 21st, 2010 at 1:15 am
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.
May 21st, 2010 at 4:19 am
Where do the dot images go?
May 21st, 2010 at 8:48 am
This is not using any private APIs.
May 28th, 2010 at 6:22 pm
Where do the dot images go?
June 10th, 2010 at 10:40 am
Thanx man…this works like a charm
June 18th, 2010 at 12:45 am
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
June 18th, 2010 at 1:17 am
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];
}
August 3rd, 2010 at 7:01 am
This code is very good.
Also works on iOS 4.0, but you have to use Steve’s last comment. Thanks guys!
September 3rd, 2010 at 1:32 pm
How do I increase the size of dots?
September 3rd, 2010 at 3:38 pm
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);
}
}
}
September 3rd, 2010 at 3:59 pm
A bit late I know! But I’ve just updated the original code in the post to include the fix from Steve. Thanks.