花時間寫了個Demo,包含比較全面從UIView,UILabel,UIImageView的自適應到UITextView,UITableView,UICollectionView,UIScrollView都有,可以下載學習一下。Demo地址
注意不配合demo也許會不知所云!
iOS8之後 使用AutoLayout是非常方便的,更何況iOS11都要發佈了,不會還想着支持iOS7吧!(iOS7也是支持autolayout的,只是有一些坑及與iOS8API的不同)。
iOS8以後,對於UIScrollView及UITableView,UICollectionViewCell的高度自適應解決了我們一直頭疼的變高問題!!!
如果你還不用,那麼你覺得你是偷懶了,懶得去學,其實你在開發中浪費了更多的時間與腦細胞。
UIView的自適應
UIView可以根據其subviews自動適配自己的寬高,也就是如果其subviews能有明確的足夠的約束信息,那麼view的寬高就可以被確定了
UILabel的自適應
有時候如果想UILabel的文字增多變寬變高,要如何實現?其實UILabel是一個比較特殊的元素,只要設置了寬度約束,高度就會自適應了(UILabel設置lines=0表示自動換行,具體數字是限制多少行)。如我們給UILabel設置約束寬度不大於200,然後動態改變它上邊的文字,它就自適應了高度
UIImageView
看到網上好多人問如何處理圖片自適應的問題,比如固定imageView的寬度,高度根據網絡獲取的image來動態改變。很遺憾,UIImageView並沒有自動佈局。我們可以讓後臺返回寬高,或者請求到image對象後,根據image的size來獲取寬高。然後根據寬高的縮放比例來適配
[self.imageViewRef mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.contentView);
}];
customCell.imageViewRef.image = image;
[customCell.imageViewRef mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo([UIScreen mainScreen].bounds.size.width * image.size.height / image.size.width);
}];
UITextView的高度自適應
要點:
- UITextView的高度自適應只需要不限制其height
- scrollEnabled = NO 只有不可滾動纔會自動擴展其高度
- 若textView的內容發生偏移,請看UITextView內容偏移的問題處理
- (UITextView *)textView
{
if (!_textView) {
_textView = [[UITextView alloc] init];
_textView.backgroundColor = [UIColor grayColor];
_textView.scrollEnabled = NO;
_textView.font = [UIFont systemFontOfSize:20];
}
return _textView;
}
- (void)makeContraints
{
WEAKSELF
[self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(weakSelf.view);
make.top.mas_equalTo(100);
make.height.greaterThanOrEqualTo(@100);
}];
}
UITableViewCell的自適應
*兼容iOS7 時 :
用systemLayoutSizeFittingSize
來取高。步驟是先在數據model中添加一個height的屬性用來緩存高,然後在table view的heightForRowAtIndexPath代理裏static一個只初始化一次的Cell實例,然後根據model內容填充數據,最後根據cell的contentView的systemLayoutSizeFittingSize的方法獲取到cell的高,然後緩存給model的height
//還是那個熟悉的高度的代理,iOS8之後就少用它吧
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
static CustomCell *cell;
//只初始化一次cell
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cell = [tableView dequeueReusableCellWithIdentifier:"CustomCell"];
});
DataModel *model = self.dataArray[(NSUInteger) indexPath.row];
[cell makeupData:model];
if (model.cellHeight <= 0) {
//使用systemLayoutSizeFittingSize獲取高度
model.cellHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1;
}
return model.cellHeight;
}
在 iOS 8 中,cell 如果有一個確定的寬度/高度,autolayout 會自動根據 cell 中的內容計算出對應的高度/寬度。
(目前不需要適配iOS7了,iOS11之後iOS8的適配也快拋棄了)
要讓 table view 的 cell 自適應內容,有幾個要點:
設置的 AutoLayout 約束必須讓 cell 的 contentView 知道如何自動延展。關鍵點是 contentView 的 4 個邊都要設置連接到內容的約束,並且內容是會動態改變尺寸的。
UITableView 的 rowHeight 的值要設置爲 UITableViewAutomaticDimension
實現tableView.estimatedRowHeight屬性或者 estimatedHeightForRowAtIndexPath代理。
//需要設置的兩句代碼:一點計算 cell 高度的代碼都沒有!!連heightForRowAtIndexPath都不用實現
_tableView.estimatedRowHeight = 80;
_tableView.rowHeight = UITableViewAutomaticDimension;
UITextView 在Cell中的高度自適應
需要textView高度自適應的需求時常表現爲—UITextView嵌套在Cell中
:
// TextViewCell.m
//cell初始化
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.contentView addSubview:self.textView];
__weak typeof(self) weakSelf = self;
[self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(weakSelf.contentView);
}];
}
return self;
}
-(UITextView *)textView
{
if (!_textView) {
_textView = [[UITextView alloc]init];
_textView.backgroundColor = [UIColor greenColor];
_textView.font = [UIFont systemFontOfSize:20];
//scrollEnabled 必須設置爲NO,否則sizeToFit適配不了
_textView.scrollEnabled = NO;
_textView.delegate = self;
}
return _textView;
}
//實現變高的關鍵所在
-(void)textViewDidChange:(UITextView *)textView
{
[textView sizeToFit];
UITableView *tableView = [self tableView];
[tableView beginUpdates];
[tableView endUpdates];
}
- (UITableView *)tableView
{
UIView *tableView = self.superview;
while (![tableView isKindOfClass:[UITableView class]] && tableView) {
tableView = tableView.superview;
}
return (UITableView *)tableView;
}
//tableView的代碼
-(UITableView *)tableView
{
if (!_tableView) {
_tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
[_tableView registerClass:[TextViewCell class] forCellReuseIdentifier:@"TextViewCell"];
//自適應cell需要的代碼
_tableView.estimatedRowHeight = 40;
_tableView.rowHeight = UITableViewAutomaticDimension;
}
return _tableView;
}
UICollectionViewCell的自適應
在 collection view 中也能讓 cell 自適應內容大小,如果 UICollectionView 的 layout 是一個 UICollectionViewFlowLayout,只需要將 layout.itemSize = … 改成 layout.estimatedItemSize = …。 只要設置了 layout 的 estimatedItemSize,collection view 就會根據 cell 裏面的 autolayout 約束去確定cell 的大小
原理:
collection view 根據 layout 的 estimatedItemSize 算出估計的 contentSize,有了 contentSize collection view 就開始顯示
collection view 在顯示的過程中,即將被顯示的 cell 根據 autolayout 的約束算出自適應內容的 size
layout 從 collection view 裏獲取更新過的 size attribute
layout 返回最終的 size attribute 給 collection view
collection 使用這個最終的 size attribute 展示 cell
//只需要實現估算高度,cell會根據其subviews自動獲取大小。
//也就是說cell的subviews需要有足夠的約束信息推斷出cell的size
layout.estimatedItemSize = CGSizeMake(SCREEN_WIDTH/2 , 150);
UIScrollView的自適應
UIScrollView也可以自適應,自適應UIScrollView就不需要我們再計算contentSize了。
但是要注意的是:UIScrollView的contentSize是根據它的subView來計算的,也就是其subViews自身必須能夠確定大小。
UIView *container = [UIView new];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
//這裏的寬度的意思是:contentSize的寬固定,高度變化,豎直方向滑動
make.width.equalTo(scrollView);
}];
總結:
AutoLayout 已經是勢在必行了,曾經它增加了代碼量,使很多人還停留在frame不捨得轉過來,現在呢,它可以極大的方便開發者,還可以省很多代碼。只要多加練習,對於iOS的佈局就不會再那麼恐怖了!