Search code examples
iosuikituibuttonorientationcgaffinetransform

UIButton (w/image) reacts strangely to device orientation after applying rotation-based CGAffineTransform


So, I have a UIButton (made up of an image) to which I apply a CGAffineTransform when tapped in order to highlight which state it is in:

- (void)animateMasterAddButton
{
    CGAffineTransform buttonTransform;

    // Button state hasn't been changed at this point, so selected == NO applies to when the button is *about* to be selected 
    if(self.masterAddButton.selected == NO)
    {
        CGAffineTransform buttonRotationTransform = CGAffineTransformMakeRotation((135.0 * M_PI) / 180);
        buttonTransform = CGAffineTransformConcat(CGAffineTransformIdentity, buttonRotationTransform);
    }
    else
    {
        buttonTransform = CGAffineTransformIdentity;
    }

    [UIView animateWithDuration: 0.3
                     animations: ^{
                         self.masterAddButton.transform = buttonTransform;
                     }];
}

This works fine if I keep the device in the same orientation. However, once I turn the device, the UIButton will disappear, but only if the UIButton is selected (i.e., only when the rotation transform is in effect). I have logged the button's frame and bounds in the view controller's willRotateToInterfaceOrientation:duration: method, with the following results:

Rotate device with button unselected:

Frame: {{8, 8}, {57, 57}}
Bounds: {{0, 0}, {57, 57}} (these are the correct values)

Rotate device with button selected:

Frame: {{-4, -4}, {80, 80}}
Bounds: {{0, 0}, {0, 113.137}}

In the second set of results, the frame is correct, as the rotated image has its corners out, so will take up more space. The bounds, however, show something is screwy. I'm guessing that as the bounds width is zero, the button is there but too narrow to render.

Can anyone enlighten me as to why this happens so, and how I can fix it?


Solution

  • Nailed it, with a bit of sideways thinking. Basically, once the rotation animation completes, the transformed button image is replaced with a version rotated by the same amount as applied by the animation (I wrote a UIImage category method to perform this process). The transform on the button is then disabled using CGAffineTransformIdentity. As no transform exists on the button when the device is rotated, it stays put :-)

    Here's the code:

    - (void)animateMasterAddButton
    {
        CGFloat addButtonRotationAngle = AddButtonRotationAngle * -1.0;    // AddButtonRotationAngle is a const float defined at the top of .m file
        CGAffineTransform buttonTransform;
        UIImage *rotatedAddButtonImage = [UIImage imageNamed: @"Add button"];
    
        if(self.masterAddButton.selected == NO)
        {
            addButtonRotationAngle =  AddButtonRotationAngle;
            rotatedAddButtonImage = [UIImage rotatedImageFromImage: rotatedAddButtonImage withAngle: (addButtonRotationAngle * M_PI) / 180];
            // rotatedImageFromImage:withAngle: is a self-written category method on UIImage
        }
    
        CGAffineTransform buttonRotationTransform = CGAffineTransformMakeRotation((addButtonRotationAngle * M_PI) / 180);
        buttonTransform = CGAffineTransformConcat(CGAffineTransformIdentity, buttonRotationTransform);
    
        [UIView animateWithDuration: 0.3
                         animations: ^{
                             self.masterAddButton.transform = buttonTransform;
                         }
                         completion: ^(BOOL finished) {
                             [self.masterAddButton setImage: rotatedAddButtonImage forState: UIControlStateNormal];
                             self.masterAddButton.transform = CGAffineTransformIdentity;
                         }];
    }