11

In my efforts to upgrade my application to support IOS7 I found out that UIPageControl doesn't support the UIImageView. They have changed it.

I'm subclassing the UIPageControl in order to put custom circles instead the regular ones (attached an example)

My class is:

- (id)initWithFrame:(CGRect)frame 
{
    // if the super init was successfull the overide begins.
    if ((self = [super initWithFrame:frame])) 
    { 
        // allocate two bakground images, one as the active page and the other as the inactive
        activeImage = [UIImage imageNamed:@"active_page_image.png"];
        inactiveImage = [UIImage imageNamed:@"inactive_page_image.png"];
    }
    return self;
}

// Update the background images to be placed at the right position
-(void) updateDots
{
    for (int i = 0; i < [self.subviews count]; i++)
    {
        UIImageView* dot = [self.subviews objectAtIndex:i];
        if (i == self.currentPage) dot.image = activeImage;
        else dot.image = inactiveImage;
    }
}

// overide the setCurrentPage
-(void) setCurrentPage:(NSInteger)page
{
    [super setCurrentPage:page];
    [self updateDots];
}

PageControl (the blue is the current page)

Now in the IOS7 I got the following error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView setImage:]: unrecognized selector sent to instance 0xe02ef00'

and after investigating I understood that the following code cause the error:

UIImageView* dot = [self.subviews objectAtIndex:i];
if (i == self.currentPage) dot.image = activeImage;
    else dot.image = inactiveImage;

I checked the subviews and saw that it is UIView instead of UIImageView. probably Apple changed something.

Any idea how to fix it?

ThomasCle
  • 6,792
  • 7
  • 41
  • 81
NDM
  • 944
  • 9
  • 30

5 Answers5

38

It looks like they changed the subviews to standard UIViews. I managed to work around it by doing this:

for (int i = 0; i < [self.subviews count]; i++)
{
    UIView* dotView = [self.subviews objectAtIndex:i];
    UIImageView* dot = nil;

    for (UIView* subview in dotView.subviews)
    {
        if ([subview isKindOfClass:[UIImageView class]])
        {
            dot = (UIImageView*)subview;
            break;
        }
    }

    if (dot == nil)
    {
        dot = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, dotView.frame.size.width, dotView.frame.size.height)];
        [dotView addSubview:dot];
    }

    if (i == self.currentPage)
    {
        if(self.activeImage)
            dot.image = activeImage;
    }
    else
    {
         if (self.inactiveImage)
             dot.image = inactiveImage;
    }
}
ThomasCle
  • 6,792
  • 7
  • 41
  • 81
1

maybe dot isn't kind of UIImageView, so try like this

UIImageView* dot = [self.subviews objectAtIndex:i];
if ([dot isKindOfClass:[UIImageView class]]) {
    if (i == self.currentPage) 
        dot.image = activeImage;
    else 
        dot.image = inactiveImage;
}
levi
  • 11
  • 3
1

I've got a bit cleaner solution:

for (int i = 0; i < [self.subviews count]; i++) {
    UIView *dotView = [self.subviews objectAtIndex:i];
    if ([dotView isKindOfClass:[UIImageView class]]) {
        UIImageView* dot = (UIImageView*)dotView;
        dot.frame = CGRectMake(dot.frame.origin.x, dot.frame.origin.y, _activeImage.size.width, _activeImage.size.height);
        if (i == self.currentPage)
            dot.image = _activeImage;
        else
            dot.image = _inactiveImage;
    }
    else {
        dotView.frame = CGRectMake(dotView.frame.origin.x, dotView.frame.origin.y, _activeImage.size.width, _activeImage.size.height);
        if (i == self.currentPage)
            [dotView setBackgroundColor:[UIColor colorWithPatternImage:_activeImage]];
        else
            [dotView setBackgroundColor:[UIColor colorWithPatternImage:_inactiveImage]];
    }
}

The idea is instead of adding subview to UIView for iOS7 just set UIView background image.

devgeek
  • 4,117
  • 2
  • 18
  • 18
  • Hmm, this works nicely. But how could I space them apart using this method? The dots are to close together and I'm struggling to find a solution. +1 for this answer too! – Ruddy Aug 19 '14 at 09:38
  • All you need to do is to adjust the dot.frame. Just set the bigger width and place dot image in the middle. – devgeek Aug 19 '14 at 11:08
  • If the frame is bigger the image will repeat to fill space hence the "pattern" part. and you cant center the image due to that. unless im missing something? – Ruddy Aug 19 '14 at 11:56
  • Yes. You are right. We are setting background colour property. Hm... You can try to set dotView background colour to clear, set the frame and add UIImageView on top with your image. In the middle of dotView. – devgeek Aug 20 '14 at 07:36
  • I may try that, also I'm not sure why but setting the width larger on the frame doesn't seem to "push" them apart. They just overlap, any idea's on that one? Thank you for your help btw, much appreciated. – Ruddy Aug 20 '14 at 07:44
1

Just a little refactor for devgeek's solution to make it a bit more compact

for (int i = 0; i < [self.subviews count]; i++) {
    UIImage *customDotImage = (i == self.currentPage) ? _activeDot : _inactiveDot;
    UIView *dotView = [self.subviews objectAtIndex:i];
    dotView.frame = CGRectMake(dotView.frame.origin.x, dotView.frame.origin.y, customDotImage.size.width, customDotImage.size.height);
    if ([dotView isKindOfClass:[UIImageView class]]) { // in iOS 6, UIPageControl contains UIImageViews
        ((UIImageView *)dotView).image = customDotImage;
    }
    else { // in iOS 7, UIPageControl contains normal UIViews
        dotView.backgroundColor = [UIColor colorWithPatternImage:customDotImage];
    }
}
Daniele
  • 2,461
  • 21
  • 16
0

Just override layoutSubviews in your subclass of UIPageControl

- (void) layoutSubviews
{
    [super layoutSubviews];
    for (UIView* dot in self.subviews)
    {
        CGRect f = dot.frame;
        //sets all the dots to be 5x5
        f.size = CGSizeMake(5, 5);
        //need to reposition vertically as the dots get repositioned when selected
        f.origin.y = CGRectGetMidY(self.bounds) - CGRectGetHeight(f)/2;
        dot.frame = f;
        //update the cornerRadius to be sure that they are perfect circles
        dot.layer.cornerRadius = CGRectGetWidth(f)/2;
    }
}
dmorrow
  • 5,152
  • 5
  • 20
  • 31