Implementing Button Text (titleLabel) and Image (imageView) Arrangement in Vertical Order#
- Initially, I implemented this using the following method:
Creating a custom view with two properties, label and imageView, and then setting their positions and layouts. Then, adding a tap gesture recognizer and using a delegate to pass back the click method.
- Second method:
Creating a custom Button that inherits from Button, with two properties, label and imageView, and then setting the layout. This way, there is no need to add a tap gesture recognizer.
- Third method:
Directly using the titleLabel and imageView provided by the Button, writing a Category to set the arrangement of the label and image, e.g. top-bottom, left-right.
Clearly, the first two methods are not good, so let's focus on the third method:
The Button has two properties: titleEdgeInsets and imageEdgeInsets. By setting these two, we can achieve all the required styles for the Button, such as image on top, image on bottom, image on left, and image on right.
Before setting these two, we need to understand the relationship between the titleLabel and imageView on the Button (imagine the default display of image and label on the Button):
- titleEdgeInsets is the inset of the titleLabel relative to its top, bottom, left, and right, similar to tableView's contentInset;
- If there is only a title, then the titleLabel's top, bottom, left, and right are all relative to the Button;
- If there is only an image, then the imageView's top, bottom, left, and right are all relative to the Button;
- If there is both an image and a label, then the image's top, bottom, and left are relative to the Button, and the right is relative to the label. The label's top, bottom, and right are relative to the Button, and the left is relative to the label.
The above paragraph is very important#
Once we understand this, we can understand the following code:
- Create a Category for Button, the .h file is as follows, defining an Enum to determine the style of the image and label, and a method to call the settings.
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSUInteger, MKButtonEdgeInsetsStyle) {
MKButtonEdgeInsetsStyleTop, // image on top, label on bottom
MKButtonEdgeInsetsStyleLeft, // image on left, label on right
MKButtonEdgeInsetsStyleBottom, // image on bottom, label on top
MKButtonEdgeInsetsStyleRight // image on right, label on left
};
@interface UIButton (ImageTitleSpacing)
/**
* Set the layout style and spacing between the button's titleLabel and imageView
*
* @param style The layout style of the titleLabel and imageView
* @param space The spacing between the titleLabel and imageView
*/
- (void)layoutButtonWithEdgeInsetsStyle:(MKButtonEdgeInsetsStyle)style
imageTitleSpace:(CGFloat)space;
- The .m file is as follows, implementing the method
#import "UIButton+ImageTitleSpacing.h"
@implementation UIButton (ImageTitleSpacing)
- (void)layoutButtonWithEdgeInsetsStyle:(MKButtonEdgeInsetsStyle)style
imageTitleSpace:(CGFloat)space
{
// 1. Get the width and height of the imageView and titleLabel
CGFloat imageWith = self.imageView.frame.size.width;
CGFloat imageHeight = self.imageView.frame.size.height;
CGFloat labelWidth = 0.0;
CGFloat labelHeight = 0.0;
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
// In iOS 8, the titleLabel's size is 0, so we use the following method
labelWidth = self.titleLabel.intrinsicContentSize.width;
labelHeight = self.titleLabel.intrinsicContentSize.height;
} else {
labelWidth = self.titleLabel.frame.size.width;
labelHeight = self.titleLabel.frame.size.height;
}
// 2. Declare global imageEdgeInsets and labelEdgeInsets
UIEdgeInsets imageEdgeInsets = UIEdgeInsetsZero;
UIEdgeInsets labelEdgeInsets = UIEdgeInsetsZero;
// 3. Get the values of imageEdgeInsets and labelEdgeInsets based on the style and space
switch (style) {
case MKButtonEdgeInsetsStyleTop:
{
imageEdgeInsets = UIEdgeInsetsMake(-labelHeight-space/2.0, 0, 0, -labelWidth);
labelEdgeInsets = UIEdgeInsetsMake(0, -imageWith, -imageHeight-space/2.0, 0);
}
break;
case MKButtonEdgeInsetsStyleLeft:
{
imageEdgeInsets = UIEdgeInsetsMake(0, -space/2.0, 0, space/2.0);
labelEdgeInsets = UIEdgeInsetsMake(0, space/2.0, 0, -space/2.0);
}
break;
case MKButtonEdgeInsetsStyleBottom:
{
imageEdgeInsets = UIEdgeInsetsMake(0, 0, -labelHeight-space/2.0, -labelWidth);
labelEdgeInsets = UIEdgeInsetsMake(-imageHeight-space/2.0, -imageWith, 0, 0);
}
break;
case MKButtonEdgeInsetsStyleRight:
{
imageEdgeInsets = UIEdgeInsetsMake(0, labelWidth+space/2.0, 0, -labelWidth-space/2.0);
labelEdgeInsets = UIEdgeInsetsMake(0, -imageWith-space/2.0, 0, imageWith+space/2.0);
}
break;
default:
break;
}
// 4. Assign the values
self.titleEdgeInsets = labelEdgeInsets;
self.imageEdgeInsets = imageEdgeInsets;
}
I created a demo on GitHub, here is the link: Custom Button
References#
- UIButton's titleEdgeInsets and imageEdgeInsets properties to achieve the desired arrangement of images and text, this blog explains the above principles, I recommend reading it carefully to understand.
- UIButton's imageEdgeInsets and titleEdgeInsets, this blog has a GitHub link at the end, when I was writing, I didn't understand the principles, so when setting it, I referred to their code.
- How to layout a UIButton with both Image and Title, this was my initial reference, it only has the code, without the principles.