There must be a time when every iOS developer wanted to place the image of UIButton above the title. Alternative Button is here for that!

TL;DR

Alternative Button is available as a pod. You can easily add it to your project just by adding pod ‘AlternativeButton’ to your ‘Podfile’ and running pod install in your project directory.

Also you can find both Alternative Button and the example project here.

Structure

Alternative Button is a subclass of UIButton and just by overriding a few methods and a few additions, it presents its image above the title. And thanks to a new feature of Xcode 6, it can be previewed in Interface Builder. Also corner radius of its image view can be set from Attributes Inspector too.

Layout

By overriding -layoutSubviews method of UIView, we can interfere with the view hierarchy and layout of any subview of a view. So we did:

self.imageView.frame = 
    (CGRect){.origin = (CGPoint){.x = margin, 
                                 .y = margin}, 
             .size = (CGSize){.width = imageViewWidth, 
                              .height = imageViewWidth}};
self.imageView.center = 
    (CGPoint){.x = CGRectGetWidth(self.frame) / 2.0,
              .y = imageViewWidth / 2.0 + margin};
    
[self.titleLabel sizeToFit];
    
self.titleLabel.frame =
    (CGRect){.origin = (CGPoint){.x = margin,
                                 .y = imageViewWidth + 2 * margin}, 
             .size = self.titleLabel.frame.size};
self.titleLabel.center =
    (CGPoint){.x = CGRectGetWidth(self.frame) / 2.0,
              .y = CGRectGetHeight(self.frame) - CGRectGetHeight(self.titleLabel.frame) / 2.0 - margin};
imageViewWidth is a constant defined in the class and its value is 64.0

Here we just adjust the frames of both image view and the title label. We place them vertically in line and horizontally centered.

Size

-intrinsicContentSize method of UIView returns a CGSize indicating the desired size of itself. So we must override this too, like so:

[self.titleLabel sizeToFit];
    
CGFloat titleLabelWidth = CGRectGetWidth(self.titleLabel.frame) + margin * 2; // margin from both sides.
CGFloat imageViewWidth = CGRectGetWidth(self.imageView.frame) + margin * 2; // margin from both sides.
    
CGFloat height = CGRectGetHeight(self.imageView.frame) + CGRectGetHeight(self.titleLabel.frame) + margin * 3; // margin from top and bottom and distance between `imageView` and `titleLabel`.
    
return CGSizeMake(MAX(titleLabelWidth, imageViewWidth), height);

Here we first adjust the size of title label, because it can be any size depending on the title set for the button. Then we get the width of both image view and the title label adding margins to them from both sides. Again, we calculate the height of the button, adding margins from both top and bottom.

margin is a constant too, defined in the class and its value is 6.0

We return a CGSize created with the width of either image view or title label, which of them is greater, and the calculated height.

-intrinsicContentSize provides a size value to be used in Interface Builder, so that whenever ‘Size to Fit Content’ is selected from the ‘Editor’ menu, it adjusts the button’s size according to this value.

Radius

We also have a public property named imageViewRadius. This is used to round the corners of the image view. By default corner radius is set to be half of the image view’s width, so that it will be a perfect circle.

IB_DESIGNABLE & IBInspectable

These are the new keywords Xcode 6 introduced. Thanks to these we can see a preview of our custom coded view in Interface Builder!

IB_DESIGNABLE tells Interface Builder that this is a custom view and needs a preview. We add this keyword just before class declaration in our header file.

IBInspectable tells Interface Builder that the property with this keyword can be adjusted from the Attributes Inspector. We add this keyword just before the type of our property.

Result

Here is a screen shot of the example project:

I used this button as a profile button in my current project and wanted to open it to the world! So that, other developers in need of a control like this can benefit.

Feel free to contribute and let me know what you think in the comments!