- 改變按鈕內(nèi)部控件的位置
- UIButton的一些常用屬性
- 九宮格的布局
- 字典轉模型
- 封裝控件的基本步驟
- XIB封裝View
UIButton,俗稱“按鈕”
一般情況下,點擊某個控件后,會做出相應反應的都是按鈕
按鈕的功能比較多,既能顯示文字,又能顯示圖片,還能隨時調(diào)整內(nèi)部圖片和文字的位置,比如


這里對于UIButton的一些常用屬性就不在一一介紹了,本文主要作為自己在使用過程中 經(jīng)常用到的一些功能點 或者說遇到的問題 進行一個總結,方便日后 遇到相同的問題能夠馬上定位到
改變按鈕內(nèi)部控件的位置
我們知道普通的按鈕都是圖片在左文字在右, 但是開發(fā)中我們經(jīng)常會遇到一些特殊的需求,比如上面列舉的圖片, 圖片在上文字在下的效果,這里我們就來總結一下常見的幾種實現(xiàn)方案吧
方案一:
重寫如下方法來對按鈕內(nèi)部的子控件進行布局:
- (CGRect)contentRectForBounds:(CGRect)bounds;
- (CGRect)titleRectForContentRect:(CGRect)contentRect;
- (CGRect)imageRectForContentRect:(CGRect)contentRect;
方案二:
重寫layoutSubviews方法,在該方法內(nèi)部對按鈕內(nèi)部的子控件進行布局:
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat buttonW = self.frame.size.width;
CGFloat buttonH = self.frame.size.height;
CGFloat imageH = buttonW - 10;
self.imageView.frame = CGRectMake(0, 0, buttonW, imageH);
self.titleLabel.frame = CGRectMake(0, imageH, buttonW, buttonH - imageH);
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
}
方案三:
該方案也是我個人比較喜歡的一種方案,其實就是直接操作titleEdgeInsets和imageEdgeInsets
1. 普通按鈕的樣子: 圖片在左,文字在右

其實對應的代碼就是:
self.btnScan.titleEdgeInsets = UIEdgeInsetsZero;
self.btnScan.imageEdgeInsets = UIEdgeInsetsZero;
2. 文字圖片都居中顯示
self.btnScan.titleEdgeInsets = UIEdgeInsetsMake(0, -self.btnScan.imageView.frame.size.width, 0, 0);
self.btnScan.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -self.btnScan.titleLabel.frame.size.width);

3. 文字在左圖片在右
self.btnScan.titleEdgeInsets = UIEdgeInsetsMake(0, -self.btnScan.imageView.frame.size.width - self.btnScan.frame.size.width + self.btnScan.titleLabel.intrinsicContentSize.width, 0, 0);
self.btnScan.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -self.btnScan.titleLabel.frame.size.width - self.btnScan.frame.size.width + self.btnScan.imageView.frame.size.width);

4. 圖片在上,文字在下
self.btnScan.titleEdgeInsets = UIEdgeInsetsMake(0, -self.btnScan.imageView.frame.size.width, -self.btnScan.imageView.frame.size.height, 0);
self.btnScan.imageEdgeInsets = UIEdgeInsetsMake(-self.btnScan.titleLabel.intrinsicContentSize.height, 0, 0, -self.btnScan.titleLabel.intrinsicContentSize.width);

如果感覺距離有些近,我們可以調(diào)節(jié)間距
CGFloat offset = 40.0f;
self.btnScan.titleEdgeInsets = UIEdgeInsetsMake(0, -self.btnScan.imageView.frame.size.width, -self.btnScan.imageView.frame.size.height-offset/2, 0);
self.btnScan.imageEdgeInsets = UIEdgeInsetsMake(-self.btnScan.titleLabel.intrinsicContentSize.height-offset/2, 0, 0, -self.btnScan.titleLabel.intrinsicContentSize.width);

設置按鈕的背景圖片
為了保證高亮狀態(tài)下的圖片正常顯示,必須設置按鈕的type為custom
UIButton *loginBtn = [UIButton buttonWithType:UIButtonTypeCustom];

讓按鈕無法點擊的2種方法
button.enabled = NO; //會進入UIControlStateDisabled狀態(tài)
button.userInteractionEnabled = NO; //不會進入UIControlStateDisabled狀態(tài),繼續(xù)保持當前狀態(tài)
設置按鈕背景圖片和圖片的區(qū)別
// 設置背景圖片 圖片會被拉伸充滿整個btn.frame
[btn setBackgroundImage:[UIImage imageNamed:@""] forState:UIControlStateNormal];
// 設置按鈕上的圖片,圖片不會被拉伸,原比例顯示在btn.frame)
[btn setImage:[UIImage imageNamed:@""] forState:UIControlStateNormal];
點擊按鈕的時候是否會發(fā)光
// 當點擊按鈕的時候是否會發(fā)光
// default is NO. if YES, show a simple feedback (currently a glow) while highlighted.
btn.showsTouchWhenHighlighted=NO;
設置內(nèi)容、標題或者是圖標間距
//contentEdgeInsets titleEdgeInsets imageEdgeInsets
btn.contentEdgeInsets=UIEdgeInsetsMake(10,0,0,0);
設置按鈕字體大小
[btn.titleLabel setFont:[UIFont systemFontOfSize:18]];
self.titleLabel.font = [UIFont boldSystemFontOfSize:22];
設置按鈕禁用或者高亮時候按鈕圖標是否變灰色
// default is YES. if YES, image is drawn darker when highlighted(pressed)
btn.adjustsImageWhenHighlighted=NO;
// default is YES. if YES, image is drawn lighter when disabled
btn.adjustsImageWhenDisabled=NO;
九宮格的布局

這里不討論用CollectionView實現(xiàn),主要就是一些簡單的計算來實現(xiàn)我們想要的效果:
CGFloat shopW = 80;
CGFloat shopH = 90;
NSInteger index = self.shopsView.subviews.count;
CGFloat margin = (self.shopsView.frame.size.width - shopW*columnNum)/(columnNum -1);
//計算行和列
NSInteger column = index % columnNum;
NSInteger row = index / columnNum;
//計算X和Y的值
CGFloat shopX = column * (shopW + margin);
CGFloat shopY = row * (shopH +margin);
//創(chuàng)建View
GSShopView *shopView = [[GSShopView alloc] init];
shopView.frame = CGRectMake(shopX, shopY, shopW, shopH);
[self.shopsView addSubview:shopView];
字典轉模型
字典轉模型的過程最好封裝在模型內(nèi)部,同時模型應該提供一個可以傳入字典參數(shù)的構造方法
#import <Foundation/Foundation.h>
@interface GSShop : NSObject
/** 名稱*/
@property (nonatomic,copy) NSString *name;
/** 圖片*/
@property (nonatomic,copy) NSString *icon;
//instancetype:編譯器會檢測instancetype的真實類型
+ (instancetype)shopWithDict:(NSDictionary *)dic;
- (instancetype)initWithDict:(NSDictionary *)dic;
@end
#import "GSShop.h"
@implementation GSShop
+ (instancetype)shopWithDict:(NSDictionary *)dic{
return [[self alloc] initWithDict:dic];
}
-(instancetype)initWithDict:(NSDictionary *)dic{
if (self = [super init]) {
self.name = dic[@"name"];
self.icon = dic[@"icon"];
}
return self;
}
@end
封裝控件的基本步驟
- 在
initWithFrame:方法中添加子控件,提供便利構造方法 - 在
layoutSubviews方法中設置子控件的frame(一定要調(diào)用super的layoutSubviews) - 增加模型屬性,在模型屬性set方法中設置數(shù)據(jù)到子控件上
這里以上面九宮格圖片為例子,封裝一個商品數(shù)據(jù)的代碼:
/**
1.0 在該方法中初始化控件
init方法內(nèi)部會自動調(diào)用initWithFrame:方法
*/
-(instancetype)initWithFrame:(CGRect)frame{
if(self = [super initWithFrame:frame]){
//1.0 創(chuàng)建icon
UIImageView *iconView = [[UIImageView alloc] init];
[self addSubview:iconView];
self.iconView = iconView;
//2.0 創(chuàng)建名稱
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.font = [UIFont systemFontOfSize:11];
nameLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:nameLabel];
self.nameLabel = nameLabel;
}
return self;
}
//2.0 布局Frame
-(void)layoutSubviews{
[super layoutSubviews];
CGFloat width = self.frame.size.width;
CGFloat height = self.frame.size.height;
self.iconView.frame = CGRectMake(0, 0, width, width);
self.nameLabel.frame = CGRectMake(0, width, width, height-width);
}
//3.0 給數(shù)據(jù)賦值
-(void)setShop:(GSShop *)shop{
_shop = shop;
self.iconView.image = [UIImage imageNamed:shop.icon];
self.nameLabel.text = shop.name;
}
當然如果考慮到性能,我們也可以將使用的控件進行懶加載創(chuàng)建,這樣 使用到的時候才會去創(chuàng)建該控件,就不用在調(diào)用initWithFrame:方法了
#pragma mark 控件懶加載 這樣更加節(jié)約性能,用到的時候再去創(chuàng)建控件;
-(UIImageView *)iconView{
if (!_iconView) {
UIImageView *iconView = [[UIImageView alloc] init];
[self addSubview:iconView];
_iconView = iconView;
}
return _iconView;
}
-(UILabel *)nameLabel{
if(!_nameLabel){
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.font = [UIFont systemFontOfSize:11];
nameLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:nameLabel];
_nameLabel = nameLabel;
}
return _nameLabel;
}
XIB封裝View
上面封裝控件是通過代碼來實現(xiàn)的,其實有時候我們利用XIB來封裝控件速度也是非常的快,這里我們來貼一下通過XIB封裝控件的代碼
#import <UIKit/UIKit.h>
@class GSShop;
@interface GSShopView2 : UIView
/**模型對象*/
@property (nonatomic, strong) GSShop *shop;
//為了讓外界在調(diào)用的時候方便可以提供兩個創(chuàng)建View的方法
+ (instancetype) shopViewWithShop:(GSShop *)shop;
+ (instancetype) shopView;
@end
#import "GSShopView2.h"
#import "GSShop.h"
@interface GSShopView2()
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@end
@implementation GSShopView2
+(instancetype)shopView{
return [self shopViewWithShop:nil];
}
+(instancetype)shopViewWithShop:(GSShop *)shop{
//加載xib文件
GSShopView2 *shopView = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
shopView.shop = shop;
return shopView;
}
-(void)setShop:(GSShop *)shop{
_shop = shop;
self.iconView.image = [UIImage imageNamed:shop.icon];
self.nameLabel.text = shop.name;
}
@end
注意點
一個控件有2種創(chuàng)建方式
通過代碼創(chuàng)建
初始化時一定會調(diào)用initWithFrame:方法通過xib\storyboard創(chuàng)建
初始化時不會調(diào)用initWithFrame:方法,只會調(diào)用initWithCoder:方法,初始化完畢后會調(diào)用awakeFromNib方法
所以當我們封裝一個控件給別人使用的時候,最好是如下操作,這樣不管是通過代碼還是xib都可以
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
[self setupUI];
}
return self;
}
-(void)awakeFromNib{
[self setupUI];
}