目錄:
- cell生命周期
- masonry數(shù)組布局
- tableview中如何讓行高自動(dòng)被撐起來
- scrollView緩慢滑動(dòng)到邊界松手不走scrollViewDidEndDecelerating
- UIButton的EdgeInsets來調(diào)整圖片文字位置
- UITableViewCell的子view如果超過了cell的邊界,會(huì)導(dǎo)致點(diǎn)擊穿透
- sizeToFit和sizeThatFits
- masonry和frame不要混用
1. cell生命周期
cell的生命周期可以參考http://www.itdecent.cn/p/59b8bb25f16a
對(duì)于cell而言- (void)prepareForReuse是先于tableview的cellForIndexPath delegate的,當(dāng)tableview第一次出現(xiàn)沒有任何滑動(dòng)的時(shí)候,prepareForReuse是不會(huì)被調(diào)用的,但是當(dāng)你稍微滑動(dòng)一丟丟,就會(huì)有prepareForReuse的調(diào)用了,但是這個(gè)時(shí)候沒有任何cell出屏幕啊,所以其實(shí)tableview是有準(zhǔn)備出來一兩個(gè)備用cell在屏幕外池子里面的~
當(dāng)tableview第一次出現(xiàn)的時(shí)候分別給cell的prepareForReuse以及delegate的三個(gè)方法(如下)打斷點(diǎn)發(fā)現(xiàn):
- prepareForReuse沒有被調(diào)用
- cellForRowAtIndexPath和willDisplayCell成對(duì)調(diào)用(多余屏幕內(nèi)個(gè)數(shù)的次數(shù),例如12次,但屏幕只放的下9個(gè))
- didEndDisplayingCell也會(huì)被調(diào)用多次,屏幕內(nèi)放得下9個(gè)cell的話,會(huì)回調(diào)didEndDisplayingCell給indexpath給10-12行的,所以其實(shí)tableview有提前準(zhǔn)備cell
當(dāng)cell要展示的時(shí)候,會(huì)回調(diào)table view的delegate willDisplayCell,然后當(dāng)cell完全出屏幕的時(shí)候會(huì)回調(diào)didEndDisplayingCell。
// delegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableview dequeueReusableCellWithIdentifier:@"Table1ViewCell"];
return cell;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
}
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
}
我打斷點(diǎn)看了一下didEndDisplayingCell的object會(huì)接著用于prepareForReuse,然后再willDisplayCell,地址都是一致的。
2. masonry數(shù)組布局
masonry可以給NSArray的view數(shù)組布局,讓他們沿著一個(gè)軸均勻分布:
@implementation NSArray (MASAdditions)
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
但是這種只能view的寬度/高度都一樣,看源碼可以看到:
MAS_VIEW *tempSuperView = [self mas_commonSuperviewOfViews];
if (axisType == MASAxisTypeHorizontal) {
MAS_VIEW *prev;
for (int i = 0; i < self.count; i++) {
MAS_VIEW *v = self[I];
[v mas_makeConstraints:^(MASConstraintMaker *make) {
if (prev) {
make.width.equalTo(prev);
所以其實(shí)上面的方法主要適用于固定spacing然后幾個(gè)view平分空間,或者view的寬度(水平分布)/高度(豎直分布)固定,平分spacing,反正得是寬度/高度一致。
雖然上面的不是很實(shí)用,不過這個(gè)分類里面還有可以給數(shù)組加一樣或者更新一樣的constraint哦~
mas_makeConstraints、mas_updateConstraints、mas_remakeConstraints
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block {
NSMutableArray *constraints = [NSMutableArray array];
for (MAS_VIEW *view in self) {
NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views");
[constraints addObjectsFromArray:[view mas_makeConstraints:block]];
}
return constraints;
}
我們經(jīng)常出現(xiàn)的場(chǎng)景其實(shí)是spacing固定,寬度/高度不一樣的沿著某個(gè)軸布局,我們可以借鑒masonry數(shù)組的思想統(tǒng)一布局改寫一個(gè)分類:
- (void)mas_live_updateDistributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing {
MAS_VIEW *tempSuperView = [self mas_commonSuperviewOfViews];
if (!tempSuperView) {
return;
}
if (axisType == MASAxisTypeHorizontal) {
MAS_VIEW *prev;
for (int i = 0; i < self.count; i++) {
MAS_VIEW *v = self[I];
[v mas_updateConstraints:^(MASConstraintMaker *make) {
if (prev) {
make.left.equalTo(prev.mas_right).offset(fixedSpacing);
} else {//first one
make.left.equalTo(tempSuperView).offset(leadSpacing);
}
make.top.bottom.equalTo(tempSuperView);
}];
if (i == self.count - 1) {
[tempSuperView mas_updateConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(v.mas_right).offset(tailSpacing);
}];
}
prev = v;
}
}
else {
MAS_VIEW *prev;
for (int i = 0; i < self.count; i++) {
MAS_VIEW *v = self[I];
[v mas_updateConstraints:^(MASConstraintMaker *make) {
if (prev) {
make.top.equalTo(prev.mas_bottom).offset(fixedSpacing);
} else {//first one
make.top.equalTo(tempSuperView).offset(leadSpacing);
}
make.right.left.equalTo(tempSuperView);
}];
if (i == self.count - 1) {
[tempSuperView mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(v.mas_bottom).offset(tailSpacing);
}];
}
prev = v;
}
}
}
這樣的話,你可以給每個(gè)item不一樣的寬高限制,這個(gè)方法只會(huì)給他們加spacing的限制,不會(huì)改變他們?cè)械膶捀撸⑶疫@個(gè)方法還能用子view撐起這個(gè)view array的共同父view容器。
(P.S. 其實(shí)有道算法題目就是找最近共同父view,思想和找鏈表最近節(jié)點(diǎn)是一樣的,找到最上面然后算差,錯(cuò)開出發(fā)就可以了)
- 其實(shí)最開始代碼不是這么寫的,是給最后一個(gè)item加right contraint為
父view right - tailSpacing,但是這樣會(huì)有一個(gè)cell復(fù)用的bug哦~
假設(shè)上一個(gè)cell有兩個(gè)item view A & B,即將出現(xiàn)的cell內(nèi)有三個(gè)view A & B & C;之前的constraint加上的是viewB.right = superView.right - tailing,然后復(fù)用的時(shí)候viewB.right = viewC.left - spacing,這里viewB木有問題可以正常顯示,但是viewC加了一個(gè)viewC.right = superView.right - tailing,于是superView現(xiàn)在有兩個(gè)right的限定,viewB顯示正常viewB.right = superView.right - tailing是生效的,那么由于viewC.right = superView.right - tailing也存在,算了一下以后viewC被擠到寬度為0了,于是就不顯示了。
其實(shí)這個(gè)問題就是要復(fù)用的時(shí)候把superView的tailing約束清掉就可以了,如果是給最后一個(gè)item加約束,由于superView是被動(dòng)加了約束,是不能用remake清掉別人給它的約束的,所以改成給superView加約束,然后cellForRowAtIndexPath的時(shí)候把superView除了tailing的約束remake一下,這樣tailing就被清掉了于是就木有問題啦~
3. tableview中如何讓行高自動(dòng)被撐起來
可以參考:http://www.itdecent.cn/p/5f5c550d61a0
懶楊楊大概說下步驟吧~
設(shè)置tableView.rowHeight = UITableViewAutomaticDimension
如此設(shè)置之后,就不必要寫heightForRowAtIndexPath方法了。(默認(rèn)值就是UITableViewAutomaticDimension)設(shè)置tableView.estimatedRowHeight = 100
設(shè)置一個(gè)預(yù)估的行高,為了代碼的易讀性,還是盡量要設(shè)置一個(gè)跟cell的高差不多的值讓內(nèi)容自動(dòng)撐開cell的contentView
// cell .m
#import "Table1ViewCell.h"
#import "Masonry.h"
@implementation Table1ViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.contentLabel = [[UILabel alloc] init];
self.contentLabel.numberOfLines = 0;
self.contentLabel.font = [UIFont systemFontOfSize:15.0];
self.contentLabel.textColor = [UIColor blackColor];
[self.contentView addSubview:self.contentLabel];
[self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.contentView).offset(10);
make.bottom.equalTo(self.contentView).offset(-10);
make.left.equalTo(self.contentView).offset(10);
make.right.equalTo(self.contentView).offset(-10);
}];
}
return self;
}
@end
// vc.m
#import "Table1ViewController.h"
#import "Table1ViewCell.h"
@interface Table1ViewController () <UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) NSArray<NSString *> *objects;
@property (weak, nonatomic) IBOutlet UITableView *tableview;
@end
@implementation Table1ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableview.dataSource = self;
self.tableview.delegate = self;
self.tableview.rowHeight = UITableViewAutomaticDimension;
self.tableview.estimatedRowHeight = 100;
self.objects = @[@"美商務(wù)部對(duì)中興通訊暫時(shí)部分解禁,中興新掌門李自學(xué):盡快恢復(fù)生產(chǎn)",
@"公告稱:從公告發(fā)布之日起至8月1日,在有限條件下解除對(duì)中興通訊公司的出口禁令。美國商務(wù)部公共事務(wù)總監(jiān)麗貝卡·格洛弗在接受CGTN記者問詢時(shí)表示,對(duì)中興通訊的7年禁售令并未正式取消。",
@"這份公告的授權(quán)對(duì)象是已經(jīng)與中興通訊開展業(yè)務(wù)的公司,期限是一個(gè)月。這些公司銷售給中興通訊的產(chǎn)品必須用于以下方面。第一是支持現(xiàn)有網(wǎng)絡(luò)和設(shè)備的持續(xù)運(yùn)行,其次是支持現(xiàn)有的手機(jī),第三是用于網(wǎng)絡(luò)安全研究和漏洞披露,另外還有一個(gè)條件是交易資金必須在美國商務(wù)部授權(quán)的機(jī)構(gòu)間轉(zhuǎn)移。",
@"在6月29日上午舉行的中興通訊股東大會(huì)上,中興新掌門人李自學(xué)發(fā)言時(shí)強(qiáng)調(diào):“我們的任務(wù)還是提振整個(gè)公司的信心,包括公司的員工(信心),在拒絕令解除之后,盡快恢復(fù)生產(chǎn),在這之后再做一些工作?!?,
@"美國芯片巨頭美光部分產(chǎn)品在華遭禁售",
@"7 月 3 日福州中級(jí)法院裁定對(duì)美國芯片巨頭美光(Micron)發(fā)出“訴中禁令”,美國部分閃存 SSD 和內(nèi)存條 DRAM 將暫時(shí)遭禁止在中國銷售,雖不是最終判決,但似乎暗示美光確實(shí)有侵權(quán)之嫌。",
@"美光方面周二表示,尚未收到競(jìng)爭(zhēng)對(duì)手臺(tái)聯(lián)電早些時(shí)候提到的在中國大陸銷售芯片的臨時(shí)禁令。美光表示,在評(píng)估來自福州中級(jí)人民法院的文件之前,該公司不會(huì)對(duì)此置評(píng)",
@"小米或成史上最快納入恒生指數(shù)的港股,一手中簽率100%",
@"自小米宣布IPO以來,這家超級(jí)獨(dú)角獸就在不停地創(chuàng)造紀(jì)錄。",
@"先是將成首家同股不同權(quán)的港股上市公司;招股書又披露在全球營收超千億且盈利的互聯(lián)網(wǎng)公司中,小米增速排名第一;認(rèn)購結(jié)束后,小米錄得近10倍超購,打破2011年以來大盤股認(rèn)購倍數(shù)紀(jì)錄,成為全球最大散戶規(guī)模的IPO,是香港有史以來規(guī)模最大的民營公司IPO,全球第三的科技公司IPO",
@"業(yè)內(nèi)人士表示,小米上市后或?qū)⒃?0個(gè)交易日后被納入恒生綜合指數(shù),創(chuàng)下港股史上最快紀(jì)錄",
@"此外,香港信報(bào)消息顯示援引市場(chǎng)消息顯示,小米一手中簽率100%,逾2.5萬人申請(qǐng)一手;至于獲分派2手有1.5萬人,認(rèn)購9手穩(wěn)獲2手。至于B組的大戶方面,最大一張飛為認(rèn)購1000萬股,獲分97.2萬股,即4860手。小米將于7月9日掛牌"];
}
#pragma mark - UITableViewDelegate
//- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// return 60;
//}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.objects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = @"Table1ViewCell";
Table1ViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[Table1ViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
cell.contentLabel.text = self.objects[indexPath.row];
return cell;
}
以上代碼跑出來是醬紫的~

如果你把注釋調(diào)的heightForRowAtIndexPath放開就是醬紫的了:

所以如果想自適應(yīng)就不要實(shí)現(xiàn)
heightForRowAtIndexPath哦~~
如果你有些cell想固定高度,有些想自適應(yīng)就醬紫吧:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
BaseItem *item = [self.dataManager cellItemAtIndexPath:indexPath];
if (item.cellHeight > 0) {
return item.cellHeight;
}
return UITableViewAutomaticDimension;
}
4. scrollView緩慢滑動(dòng)到邊界松手不走scrollViewDidEndDecelerating
可參考:https://blog.csdn.net/abc649395594/article/details/46780065/
場(chǎng)景就是如果你緩慢滑動(dòng),一直滑到該停下的位置,那么其實(shí)你停下是被迫的,沒有減速過程,不會(huì)調(diào)用scrollViewDidEndDecelerating,所以其實(shí)如果你想在停止?jié)L動(dòng)的時(shí)候做點(diǎn)什么,需要同時(shí)實(shí)現(xiàn)scrollViewDidEndDragging:
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
if(!decelerate){
//這里復(fù)制scrollViewDidEndDecelerating里的代碼
}
}
5. UIButton的EdgeInsets來調(diào)整圖片文字位置
當(dāng)button.width < image.width時(shí),只顯示被壓縮后的圖片,圖片是按fillXY的方式壓縮。
當(dāng)button.width > image.width,且 button.width < (image.width + text.width)時(shí),圖片正常顯示,文本被壓縮。
當(dāng)button.width > (image.width + text.width),兩者并列默認(rèn)居中顯示,可通過button的屬性contentHorizontalAlignment改變對(duì)齊方式。
// 下面實(shí)驗(yàn)的測(cè)試代碼
- (void)testButtonEdge {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.layer.cornerRadius = 4;
button.layer.borderWidth = 0.5;
button.layer.borderColor = [UIColor blueColor].CGColor;
[button setImage:[UIImage imageNamed:@"button_icon"] forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:20];
[button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[button setTitle:@"HaHa" forState:UIControlStateNormal];
button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
button.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 10);
button.imageEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 0);
button.titleEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 10);
[button sizeToFit];
button.frame = CGRectMake(100, 100, button.frame.size.width, button.frame.size.height);
button.frame = CGRectMake(100, 100, button.frame.size.width, 50);
[self.view addSubview:button];
UILabel *imageEdgeInsetsLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 160, 0, 0)];
imageEdgeInsetsLabel.text = [NSString stringWithFormat:@"(%.0f, %.0f, %.0f, %.0f)", button.titleEdgeInsets.top, button.titleEdgeInsets.left, button.titleEdgeInsets.bottom, button.titleEdgeInsets.right];
[imageEdgeInsetsLabel sizeToFit];
[self.view addSubview:imageEdgeInsetsLabel];
UILabel *textFrameLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 180, 0, 0)];
dispatch_async(dispatch_get_main_queue(), ^{
textFrameLabel.text = [NSString stringWithFormat:@"(%.1f, %.1f, %.1f, %.1f)", button.titleLabel.frame.origin.x, button.titleLabel.frame.origin.y, button.titleLabel.frame.size.width, button.titleLabel.frame.size.height];
[textFrameLabel sizeToFit];
[self.view addSubview:textFrameLabel];
});
}
(1)contentEdgeInsets
如果我們什么都不設(shè)置,一個(gè)正常的按鈕如果設(shè)置了圖片和文字,就和下圖左上角的類似,button會(huì)緊緊包裹住文字和圖片,并且title和image會(huì)緊挨著:

contentEdgeInsets可以設(shè)置文字+圖片的外圍距離button四周的inset,注意它是會(huì)改變button的大小的哦!
例如當(dāng)我們?cè)O(shè)置inset為(20,0,20,0)的時(shí)候,可以看到相比inset設(shè)為0左右就多了一樣的距離。
但是如果既設(shè)置了height,但上下inset加起來超過了height,就會(huì)變成左下角那種不知道怎么布局的布局了哦0.0

當(dāng)文字高度和圖片不一致的時(shí)候,看起來是以最外圍(高的一方)為準(zhǔn)來設(shè)置inset的,如果設(shè)置button高度很高,本身文字和圖片居中的時(shí)候距離上下的inset已經(jīng)超過了設(shè)置,就會(huì)默認(rèn)居中了,所以其實(shí)inset類似于最小邊距吧,比它大就好~
(2)contentHorizontalAlignment & contentVerticalAlignment
從字面來看,其實(shí)這個(gè)屬性就是水平和豎直方向如何對(duì)齊,它的取值如下:
typedef NS_ENUM(NSInteger, UIControlContentVerticalAlignment) {
UIControlContentVerticalAlignmentCenter = 0,
UIControlContentVerticalAlignmentTop = 1,
UIControlContentVerticalAlignmentBottom = 2,
UIControlContentVerticalAlignmentFill = 3,
};
typedef NS_ENUM(NSInteger, UIControlContentHorizontalAlignment) {
UIControlContentHorizontalAlignmentCenter = 0,
UIControlContentHorizontalAlignmentLeft = 1,
UIControlContentHorizontalAlignmentRight = 2,
UIControlContentHorizontalAlignmentFill = 3,
UIControlContentHorizontalAlignmentLeading API_AVAILABLE(ios(11.0), tvos(11.0)) = 4,
UIControlContentHorizontalAlignmentTrailing API_AVAILABLE(ios(11.0), tvos(11.0)) = 5,
};
所以其實(shí)如果改變豎直方向的對(duì)齊方式,當(dāng)設(shè)置height比內(nèi)容高很多的時(shí)候,button就不一定內(nèi)容居中了哦~

(3)imageEdgeInsets
imageEdgeInsets調(diào)整image的上下左右邊緣與content外圍的距離(content外圍和contentEdgeInsets也會(huì)有關(guān)系),該調(diào)整并不會(huì)改變UIButton原來的大小,image為了適應(yīng)調(diào)整,可能會(huì)變形或者跑出UIButton的外面。
例如以上面的情形為例,當(dāng)設(shè)置豎直方向是UIControlContentVerticalAlignmentTop,并且contentEdgeInsets = UIEdgeInsetsMake(5, 20, 5, 40),然后設(shè)置不同的imageEdgeInsets會(huì)是醬紫的:

還記得最開始那個(gè)height是50上下contentInset是5,并且豎直居中的例子么,上下inset看起來好像比5大很多,但是實(shí)際上就布局而言,除了上下5的部分都算是content了哦~
然后content內(nèi)部才會(huì)去分imageEdgeInsets以及titleEdgeInsets。這也就是為什么當(dāng)設(shè)置height為50,contentEdgeInsets為(5, 20, 5, 40),imageEdgeInsets設(shè)置(5, 0, 20, 0)或(5, 0, 0, 0)image的底部位置都是一樣的。
當(dāng)設(shè)置imageEdgeInsets為(5, 0, 30, 0)的時(shí)候,因?yàn)樯厦嬗衏ontentEdgeInsets的5+ imageEdgeInsets的5=10,然后下面還有contentEdgeInsets的5+imageEdgeInsets的30=35,所以那個(gè)圖標(biāo)的top坐標(biāo)是10,然后高度就是50-10-35=5。

需要注意的一點(diǎn)是,上下和左右是分別對(duì)應(yīng)的,可以設(shè)置負(fù)值(例如第一個(gè)參數(shù)上,填正數(shù)代表向下移動(dòng)),bottom和right都是如果是正的,就是想上/左移動(dòng)一定距離,如果是負(fù)數(shù),才是向下或右移動(dòng)距離。如果設(shè)置了top/left inset,也應(yīng)將bottom/right設(shè)置相同數(shù)字inset,但是要取負(fù)數(shù)哦。
那么right究竟是相對(duì)誰的呢?
我看了好多文章,有的說其實(shí)是相對(duì)button的,有些說是相對(duì)titleLabel的,這里我做了一些嘗試來看一下~

整體button是65的寬度,titleLabel大約50的寬度??梢钥吹疆?dāng)right設(shè)為10的時(shí)候,image根本沒有移動(dòng),當(dāng)right設(shè)為55以后,image的右側(cè)才有了一些變化,所以其實(shí)當(dāng)right是正數(shù)的時(shí)候,相對(duì)的是button右邊緣的距離,因?yàn)楸緛砭陀写蠹s50的距離,所以如果設(shè)為50以下的正數(shù),image不會(huì)變化。
如果設(shè)置的數(shù)字大于55,例如70或100,會(huì)發(fā)現(xiàn)image右邊緣貼住了button的左邊緣,image左邊緣距離button右邊緣的距離是我們寫定的right inset。
如果把right設(shè)為負(fù)數(shù),會(huì)發(fā)現(xiàn)如果設(shè)為-10,image右移了5;設(shè)為50,右移了25;設(shè)為150則右移了75。是否超過了button邊距好像對(duì)計(jì)算沒有影響,圖片也不會(huì)拉伸。
如果right是正值,他就是相對(duì)于button右邊緣的;如果超出了button寬度,image就出了左邊緣了,這個(gè)值就是image左邊緣到button右邊緣的距離;如果是負(fù)值,則是和left值平均后,相對(duì)于原來的位置的右邊緣做位移。
現(xiàn)在加上正left來看一下:

當(dāng)我們固定left inset為10,然后如果設(shè)置right為0到-10之間都沒區(qū)別的;如果right的絕對(duì)值大于了-10并且和left正負(fù)顛倒,也就是向同一方向移動(dòng),例如設(shè)為-20,那么圖片會(huì)右移(10 + 20)/2 = 15,圖片尺寸不拉伸,同理right為-70的時(shí)候圖片右移40。
如果right為正,在10到40之間對(duì)圖片都沒多大影響,因?yàn)楸緛韴D片右邊緣距離button右邊緣為(65按鈕寬度 - 10圖片左坐標(biāo) - 16圖片寬度) = 39。如果設(shè)為45,圖片左側(cè)沒有變但寬度壓縮了,右側(cè)距離button邊緣為45;如果改為50就很有意思了,由于button寬度65,左右inset加起來等于65,圖片就沒有寬度啦,于是image不顯示,僅顯示title且居中;如果設(shè)為70或100,圖片右側(cè)維持在10坐標(biāo),也就是left inset的值,左側(cè)距離button右邊緣為70或100,符合之前的結(jié)論。
現(xiàn)在加上負(fù)left來看一下:

當(dāng)left設(shè)為-10,right設(shè)為0的時(shí)候,image左移了(10 + 0) / 2 = 5,如果right為0到10的區(qū)間,image左移的距離都是(right + left) / 2的距離,例如left為-10以及right為10的時(shí)候,image左移(10 + 10) / 2 = 10。
如果right>10的時(shí)候,例如20或者40,image和right設(shè)為10的時(shí)候沒有區(qū)別,因?yàn)閞ight距離button右側(cè)距離為60;如果right設(shè)為65,那么現(xiàn)在的右側(cè)距離已經(jīng)不夠了,image就會(huì)被擠壓并且左側(cè)仍舊在-10,右側(cè)坐標(biāo)為0;如果設(shè)為75,此時(shí)image的寬度被擠壓為0不顯示,title將居中顯示;如果right設(shè)為150,圖片右側(cè)將變?yōu)閘eft inset也就是-10,左側(cè)距離button右側(cè)為150。
當(dāng)right設(shè)為-5的時(shí)候,圖片左移了(10 - 5) / 2 = 2.5的距離;right設(shè)為-30的時(shí)候,圖片左移了(10 - 30) / 2 =-10的距離,也就是右移了10距離;right設(shè)為-200的時(shí)候,圖片左移了(10 - 200) / 2 =-95的距離,也就是右移了95距離。
這里總結(jié)一下關(guān)于imageEdgeInsets的right和left設(shè)置
正right在
max(-left,0)到button右邊緣到image右邊緣距離之間的時(shí)候,image只聽left inset布局即可,不用考慮右側(cè),圖片不壓縮。正right在
button右邊緣到image右邊緣距離到button右邊緣到image左邊緣距離之間的時(shí)候,圖片左側(cè)坐標(biāo)按照left inset設(shè)置,右側(cè)坐標(biāo)距離button右邊緣為right inset的距離,圖片壓縮。正right=
button右邊緣到image左邊緣距離的時(shí)候,圖片不顯示,title居中顯示。正right大于
button右邊緣到image左邊緣距離的時(shí)候,圖片右側(cè)距離button左側(cè)距離為left inset,圖片左側(cè)距離button右側(cè)距離為right inset。負(fù)right在
0到min(0, -left)之間的時(shí)候,圖片左側(cè)根據(jù)left inset確定坐標(biāo),圖片不壓縮,右側(cè)無變化。left為0不算在內(nèi)哦。負(fù)right在
min(0, -left)到負(fù)無窮之間的時(shí)候,圖片不壓縮,圖片左側(cè)坐標(biāo)為(left inset - right inset) / 2。left為負(fù), 正right在
0到max(-left,0)之間,圖片左側(cè)坐標(biāo)為(left inset - right inset) / 2。
其實(shí)以上只是為了搞清楚,實(shí)際應(yīng)用很少這么干,如果向移動(dòng)image位置,以左側(cè)為準(zhǔn)看要移動(dòng)多少,然后right取反就行。
--
如果結(jié)合contentInset和imageInset嘞?

可以看出,當(dāng)存在contentEdgeInsets的時(shí)候,imageEdgeInsets所謂的button邊緣是除去contentEdgeInsets的部分,也就是內(nèi)圈,不是實(shí)際的button邊緣哦。
(4)titleEdgeInsets

如果只設(shè)置titleEdgeInsets的left,如果是正值,則title向右移動(dòng)left的數(shù)值,同時(shí)image會(huì)右移left/2的數(shù)值,所以當(dāng)left設(shè)為50的時(shí)候,title就移沒了,image會(huì)居中顯示。
如果設(shè)為負(fù)數(shù),那么title會(huì)左移left/2的數(shù)值,image不會(huì)動(dòng)~

講真,只設(shè)置right的時(shí)候我也木有感受到有什么規(guī)律。。有的時(shí)候titleLabel的right距離button的right是設(shè)定的數(shù)值,有的時(shí)候不是;如果超過了button(button左側(cè)坐標(biāo)沒有動(dòng),所以超過的標(biāo)準(zhǔn)就是right inset = 65 - 16 = 49),titleLabel就不顯示了,image會(huì)居中。
如果right和left都設(shè)置的話:

可以看到right和left是同一個(gè)方向(數(shù)值相反)設(shè)置,移動(dòng)距離就是兩者相加后平均;如果不是一個(gè)方向,就不一定怎么動(dòng)了0.0,雙正會(huì)比較奇怪,雙負(fù)數(shù)就是兩者抵消后平均。
應(yīng)用
- 如果你想正常的移動(dòng)image或者title,千萬要左右對(duì)稱哦,上面的亂七八糟的結(jié)果都是由于左右不對(duì)稱造成的,很難記具體的計(jì)算規(guī)則,但是只要兩者對(duì)稱其實(shí)就會(huì)正常移動(dòng)你想要的距離的。
兩個(gè)比較具體的應(yīng)用:(1)讓title在image前面;(2)讓image在上,title在下,兩者中心對(duì)齊。
我這里只做第二種啦:
- (void)testButtonEdge {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.layer.cornerRadius = 4;
button.layer.borderWidth = 0.5;
button.layer.borderColor = [UIColor blueColor].CGColor;
[button setImage:[UIImage imageNamed:@"button_icon"] forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:20];
[button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[button setTitle:@"HaHa" forState:UIControlStateNormal];
button.contentVerticalAlignment = UIControlContentVerticalAlignmentTop;
[button sizeToFit];
button.frame = CGRectMake(100, 100, button.frame.size.width, button.frame.size.height);
[self.view addSubview:button];
dispatch_async(dispatch_get_main_queue(), ^{
button.contentEdgeInsets = UIEdgeInsetsMake(button.imageView.frame.size.height / 2, 0, button.imageView.frame.size.height / 2, 0);
button.imageEdgeInsets = UIEdgeInsetsMake(-button.imageView.frame.size.height, (button.frame.size.width - button.imageView.frame.size.width) / 2, button.imageView.frame.size.height, -(button.frame.size.width - button.imageView.frame.size.width) / 2);
button.titleEdgeInsets = UIEdgeInsetsMake(button.imageView.frame.size.height, -button.imageView.frame.size.width / 2, -button.imageView.frame.size.height, button.imageView.frame.size.width / 2);
[button sizeToFit];
});
}

P.S. 講真button這幾個(gè)inset真的是玄學(xué)0.0~ 最好的方式。。就是嘗試。。。但contentEdgeInset還是可以算的畢竟只有它可以改變size
以及注意哦,我們即使改變了各種inset,button的frame.origin是沒有變的。。
6. UITableViewCell的子view如果超過了cell的邊界,會(huì)導(dǎo)致點(diǎn)擊穿透
這個(gè)場(chǎng)景是,我希望在一個(gè)cell上面加一個(gè)氣泡(氣泡位置和cell內(nèi)的button有關(guān)),但是氣泡已經(jīng)出了自身cell的邊界了,點(diǎn)擊氣泡是要消失的。
當(dāng)我把氣泡加了點(diǎn)擊手勢(shì)以后發(fā)現(xiàn)點(diǎn)擊會(huì)觸發(fā)它overlap的cell的點(diǎn)擊事件,而不會(huì)觸發(fā)氣泡的點(diǎn)擊手勢(shì),原因就是氣泡已經(jīng)出了cell的可以控制的事件范圍了。
所以最后的做法是氣泡需要加到table上面,由cell提供接口,讓table可以獲取到應(yīng)該加到什么位置(cell內(nèi)button位置),然后在cell即將display的時(shí)候調(diào)用show即可。
可以通過類型轉(zhuǎn)換來判斷cell,也可以通過confirm protocol來提供接口,但最好不要用responseTo的runtime調(diào)用,比較容易出錯(cuò)哦~
需要注意的是,數(shù)據(jù)源更新的時(shí)候最好讓氣泡消失,要不也挺麻煩的要重新找到cell,以及這個(gè)cell現(xiàn)在的位置,然后再移動(dòng)氣泡。
7. sizeToFit和sizeThatFits
可以參考:http://www.itdecent.cn/p/b10b367bd72a
總體而言就是:
sizeThatFits: 計(jì)算出最優(yōu)的size并返回,不會(huì)改變當(dāng)前view的size(僅計(jì)算)
sizeToFit: 會(huì)調(diào)用sizeThatFits,根據(jù)返回的size修改自己的size(計(jì)算+改變view)
懶得寫代碼了就借別人家的代碼了0.0
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 0, 0)];
[label setFont:[UIFont systemFontOfSize:20]];
label.text = @"北京歡迎您?。?!";
[label sizeToFit];
NSLog(@"width=%.1f height=%.1f ", label.frame.size.width, label.frame.size.height);
[self.view addSubview:label];
輸出:width=163.5 height=24.0
// 如果用下面的替換sizeToFit
CGSize sizeThatFits = [label sizeThatFits:CGSizeZero];
NSLog(@"sizeThatFits: width=%.1f height=%.1f", sizeThatFits.width, sizeThatFits.height);
NSLog(@"width=%.1f height=%.1f", label.frame.size.width, label.frame.size.height);
輸出:
sizeThatFits: width=163.5 height=24.0
width=0.0 height=0.0
- sizeThatFits的參數(shù)有啥用呢?
傳說是醬紫的:
當(dāng)使用CGSizeZero時(shí),其返回的size為正常顯示的size;當(dāng)你自定義某個(gè)size時(shí),例如:CGSizeMake(80, 80),當(dāng)計(jì)算的最合適的size比自定義的數(shù)值小時(shí),返回正常計(jì)算的size,當(dāng)計(jì)算的最合適的size比自定義的數(shù)值大時(shí),這時(shí)返回的將是在自定義size范圍內(nèi)的最合適的size。
但對(duì)label而言無論我怎么改傳入的size好像返回的都一樣0.0,button我也試了一下也是沒能實(shí)現(xiàn)用參數(shù)size來limit,可能是有些view有這么做有些木有吧-。-
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 0, 0)];
label.text = @"北京歡迎您!??!";
CGSize sizeThatFits = [label sizeThatFits:CGSizeMake(10, 500)];
NSLog(@"sizeThatFits: width=%.1f height=%.1f", sizeThatFits.width, sizeThatFits.height);
8. masonry和frame不要混用
如果用masonry布局,然后用frame做動(dòng)畫(UIView animate)有可能會(huì)產(chǎn)生奇奇怪怪的現(xiàn)象,所以盡量不要混用叭。