This article is from a brief book, the original address: http://www.jianshu.com/p/e5fdc00b8e31
Recently, we have used many button-like effects on our homepage. Before, we used to customize view, add button and label to view to achieve the same effect. But today, I teach you a very simple method. Just need to customize button, we can exchange titleLabel and image on the same bt.
The desired effect:

Actual effect: Haha, you need to beautify yourself.


Portal: https://github.com/JonesCxy/UIButton
No nonsense, go directly to the code:
category.h
#import <UIKit/UIKit.h>
@interface UIButton (Layout)
@property (nonatomic,assign) CGRect titleRect;
@property (nonatomic,assign) CGRect imageRect;
@end
category.m
#import "UIButton+Layout.h"
#import <objc/runtime.h>
@interface UIButton ()
@end
@implementation UIButton (Layout)
#pragma mark - ***************** dynamically adds associations *************** through runtime
//Define the associated Key
static const char * titleRectKey = "yl_titleRectKey";
- (CGRect)titleRect {
return [objc_getAssociatedObject(self, titleRectKey) CGRectValue];
}
- (void)setTitleRect:(CGRect)rect {
objc_setAssociatedObject(self, titleRectKey, [NSValue valueWithCGRect:rect], OBJC_ASSOCIATION_RETAIN);
}
//Define the associated Key
static const char * imageRectKey = "yl_imageRectKey";
- (CGRect)imageRect {
NSValue * rectValue = objc_getAssociatedObject(self, imageRectKey);
return [rectValue CGRectValue];
}
- (void)setImageRect:(CGRect)rect {
objc_setAssociatedObject(self, imageRectKey, [NSValue valueWithCGRect:rect], OBJC_ASSOCIATION_RETAIN);
}
#pragma mark - ***************** through runtime dynamic substitution method *************************
+ (void)load {
MethodSwizzle(self,@selector(titleRectForContentRect:),@selector(override_titleRectForContentRect:));
MethodSwizzle(self,@selector(imageRectForContentRect:),@selector(override_imageRectForContentRect:));
}
void MethodSwizzle(Class c,SEL origSEL,SEL overrideSEL)
{
Method origMethod = class_getInstanceMethod(c, origSEL);
Method overrideMethod= class_getInstanceMethod(c, overrideSEL);
//The runtime function class_addMethod fails to return if it finds that the method already exists. It can also be used for checking:
if(class_addMethod(c, origSEL, method_getImplementation(overrideMethod),method_getTypeEncoding(overrideMethod)))
{
//If the method is successfully added (overridden in the parent class), then the method in the target class is replaced by the old implementation:
class_replaceMethod(c,overrideSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
}
else
{
//AddiMethod lets the method of the target class point to the new implementation, and then uses replaceMethod to point the new method to the original implementation, thus completing the exchange operation.
method_exchangeImplementations(origMethod,overrideMethod);
}
}
- (CGRect)override_titleRectForContentRect:(CGRect)contentRect {
if (!CGRectIsEmpty(self.titleRect) && !CGRectEqualToRect(self.titleRect, CGRectZero)) {
return self.titleRect;
}
return [self override_titleRectForContentRect:contentRect];
}
- (CGRect)override_imageRectForContentRect:(CGRect)contentRect {
if (!CGRectIsEmpty(self.imageRect) && !CGRectEqualToRect(self.imageRect, CGRectZero)) {
return self.imageRect;
}
return [self override_imageRectForContentRect:contentRect];
}
- (void)setTitleRect:(CGRect )titleRect ImageRect:(CGRect )imageRect {
self.titleRect = titleRect;
self.imageRect = imageRect;
}
@end
Customize Bt.h
#import <UIKit/UIKit.h>
@interface CxyButton : UIButton
@property(assign,nonatomic)CGRect titleRect;
@property(assign,nonatomic)CGRect imageRect;
@end
Custom Bt.m
#import "CxyButton.h"
@implementation CxyButton
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(CGRect)titleRectForContentRect:(CGRect)contentRect{
if (!CGRectIsEmpty(self.titleRect) && !CGRectEqualToRect(self.titleRect, CGRectZero)) {
return self.titleRect;
}
return [super titleRectForContentRect:contentRect];
}
-(CGRect)imageRectForContentRect:(CGRect)contentRect{
if (!CGRectIsEmpty(self.imageRect)&&!CGRectEqualToRect(self.imageRect, CGRectZero)) {
return self.imageRect;
}
return [super imageRectForContentRect:contentRect];
}
@end
ViewControll.m
- (void)viewDidLoad {
[super viewDidLoad];
CxyButton *bt = [CxyButton buttonWithType:(UIButtonTypeCustom)];
bt.imageRect = CGRectMake(0, 0, 20, 20);
bt.titleRect = CGRectMake(20, 0, 80, 20);
bt.frame = CGRectMake(100, 100, 100, 20);
[bt setImage:[UIImage imageNamed:@"heart-1"] forState:(UIControlStateNormal)];
[bt setTitle:@"I am me" forState:(UIControlStateNormal)];
bt.backgroundColor = [UIColor redColor];
[self.view addSubview:bt];
CxyButton *bt2 = [CxyButton buttonWithType:(UIButtonTypeCustom)];
bt2.imageRect = CGRectMake(0, 0, 30, 30);
bt2.titleRect = CGRectMake(0, 20, 30, 80);
bt2.frame = CGRectMake(100, 150, 30, 100);
[bt2 setImage:[UIImage imageNamed:@"heart-2"] forState:(UIControlStateNormal)];
[bt2 setTitle:@"I" forState:(UIControlStateNormal)];
bt2.backgroundColor = [UIColor greenColor];
[self.view addSubview:bt2];
}
Introduction: The Title Label and image positions of BT are dynamically changed by runtime. When assigning values to bt, only the rect of title and image need to be rewritten.