0
votes

I have this 10 keypad on iPhone that has basically the same screen as the iPhone unlock screen, only mine rotates which resizes the buttons to fit the screen. The problem is that the animation of rotating the device distorts the round shape because they have changed size, but the cornerRadius is the same until it completes the animation. Once the rotation animation has completed, the buttons get the radius set again, thus making them round. I don't know how to have the UIButtons always round, specifically during the rotation animation. Here's basically what I have:

- (void)viewDidLayoutSubviews {
    [self setupButtons];
}

- (void)setupButtons {
    for (UIButton *button in self.touchPadButtons) {
        [self roundifyButton:button withRadius:radius];
    }
}

- (void)roundifyButton:(UIButton *)button {
    NSInteger height = button.frame.size.height;
    radius = height/2;
    button.layer.cornerRadius = radius;
    button.layer.borderWidth = .6f;
    button.layer.borderColor = [UIColor whiteColor].CGColor;
    button.clipsToBounds = YES;
}

I've tried using:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator

but it seems like in order for setting the radius to work from that method, I'd have to set the size of my buttons programmatically instead of using autolayout. Does anyone have any magical suggestions on handling this? I would sure love to not rotate, like Apple, but unfortunately that decision was not mine.

1

1 Answers

1
votes

Wow, this was tougher than I thought it would be. Fortunately, WWDC is going on right now and I was able to get a solution from the Interface Builder lab. His solution was to subclass UIButton and overwrite the drawRect: method. So this is the only method you should have in the CircleButton class. One issue I found is that the lineWidth property doesn't get set before it's initialized by the nib. I overwrote the init method and set a default value, but it doesn't get hit the first time when the nib initializes the buttons. So I had to add the default value in the drawRect: method. I hope this helps people who need circular UIButtons that can resize.

- (void)drawRect:(CGRect)rect {
    [[UIColor whiteColor] set];
    if (!self.lineWidth) {
        self.lineWidth = 0.75;
    }

    CGRect bounds = [self bounds];
    CGRect circleRect = CGRectMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds), 0, 0);

    CGFloat radius = MIN(bounds.size.width, bounds.size.height) / 2.0;
    circleRect = CGRectInset(circleRect, -radius, -radius);

    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(circleRect, self.lineWidth / 2.0, self.lineWidth / 2.0)];

    [path setLineWidth:self.lineWidth];
    [path stroke];
}

In case you want to animate the button click the way iPhone lock-screen does, you'll need to add this to the CircleButton class. Otherwise only the titleLabel will be highlighted.

- (void)drawRect:(CGRect)rect {
    [[UIColor whiteColor] set];
    if (!self.lineWidth) {
        self.lineWidth = 0.75;
    }

    CGRect bounds = [self bounds];
    CGRect circleRect = CGRectMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds), 0, 0);

    CGFloat radius = MIN(bounds.size.width, bounds.size.height) / 2.0;
    circleRect = CGRectInset(circleRect, -radius, -radius);
    self.layer.cornerRadius = radius;
    self.clipsToBounds = YES;

    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(circleRect, self.lineWidth / 2.0, self.lineWidth / 2.0)];

    [path setLineWidth:self.lineWidth];
    [path stroke];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // highlight button on click
    self.backgroundColor = [UIColor lightGrayColor];
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    // remove highlight
    self.backgroundColor = [UIColor clearColor];
}