AutoLayout全解
AutoLayout簡介
Autolayout是一種全新的布局技術,專門用來布局UI界麵的,用來取代Frame布局在遇見屏幕尺寸多重多樣的問題。Autolayout自iOS 6開始引入,但是由於Xcode 4的不給力,當時並沒有得到大規模推廣。在iOS 7(Xcode5)開始,Autolayout的開發效率得到很大的提升,蘋果官方也推薦開發者盡量使用Autolayout來布局UI界麵,減少純代碼的方式。
那麼AutoLayout怎麼使用呢?
VFL
VFL(Virsual Format Language)是一種虛擬的格式化語言,主要用來創建AutoLayout的約束字符串。
示例,如:V: |-(0)-Label1-(0)-Label2-(0)-| 方向:從左到右,從上到下
V:表示方向為垂直方向,也就是豎向;H為橫向。
|:豎線表示為邊界(當前所在View的邊界),這裏緊鄰方向表示符V,方向是從上到下,因此表示上麵界。
0:NSNumber 0 表示約束值為0。這裏是Label1距離上邊界的約束為0。
Label1:表示對象Label1。
0:表示Label1和Label2的約束為0.
Label2:表示對象Label2。
0:表示Label2和下邊界的約束為0.
|:表示下邊界。
關於[VFL官網]詳細知識,請查看官方的介紹
AutoLayout IB使用方式
為了讓布局能夠在不同屏幕的size上都能夠表現正常,我們需要對其增加“約束”。然後,在不同屏幕尺寸下view就能夠按照約束來局。
屬性說明:
1:距離邊緣 最上麵的4個虛線表示某個View的距離上邊 左邊 右邊 下邊多高
2:那個藍色的Constrain To Margins 是iPhone6出現之後。
Apple 覺得更大的分辨率有點間距好看, 默認為8 , 如果這個勾上了 這個View距離四周的值就變成了 你輸入的值+8。 一般建議勾掉 。
案例1
案例2
某個View距離父View的左側20,上20,寬高均為100。
注意:我在添加約束的時候有個選項叫做updateFrame 如果勾選 會直接將Frame調整到真實值 ,而不需要再次update 。
案例3
某個View距離在父View的左側20 案例2中白色View 上20 寬高和Demo2中的寬高一樣。
對其處理
屬性說明:
- Leading Edges:左對齊
- Trailing Edges:右對齊
- Top Edges:上對齊
- Bottom Edges:下對齊
- Horizontal Centers:水平中心對齊
- Vertical Centers:豎向中心對齊
- Baselines:基線對齊
- Horizontal Center in Container:對齊容器中的水平中心
- Vertical Center in Container:對齊容器中的豎向中心
案例4
某個View距離在父View的右側20 案例3中白色View上20 寬高和案例3中的寬高一樣 並且對齊。
案例5
像label 默認是有寬度的 寬度就是字體自適應的。這樣我們就可以不給UIlabel 高度 把Label的NumberOfline = 0就可以自適應高度了。
Tip
1,有時候約束太多的時候 我們可以給某個View起個假名字以起到唯一標識的作用。
2,View總是選不中怎麼辦?按 ctrl + shift + 單擊。
3,當ScrollView過長無法編輯怎麼辦?將控製器改為Freedom 修改ContentView的高度約束 這樣ScrollView 就可以滾動了。
UItableViewCell高度計算
為了方便說明,本部分知識主要從以下幾個方麵講解。
- AutoLayout with UILabel in UITableViewCell
- AutoLayout with UITextView in UITableViewCell
- Manual Layout with UILabel in UITableViewCell
- Manual Layout with UITextView in UITableViewCell
- 隨UITextView高度動態改變Cell高度
AutoLayout with UILabel
創建一個空的xib,命名為C1.xib, 然後拖入一個UITableViewCell控件。接著創建一個UITableViewCell的子類,命名為C1類。然後在C1.xib中,將與C1類進行關聯。隻需要在Class那裏寫入關聯的類名C1即可。
還有由於UITableViewCell需要重用功能,所以我們還需要設置一個重用標識。
接著我們在UITableView中來使用我們自定義的UITableViewCell C1。首先我們創建一個UITableViewController的子類T1ViewController, 接著在Main.storyboard中拖入一個UITableViewController,並關聯T1ViewController。
Auto Layout with UITextView
同樣參考上麵我們創建一個C2.xib, UITableViewCell的子類C2,並關聯C2.xib與C2類。並在C2.xib中對其布局,同樣使用了auto layout. 布局如下圖:
創始UITableViewController的了類T2ViewController,在Main.storyboard中拖入UITableViewController,並關聯他們。接著代碼中注冊C2.xib到UITableView。
如下麵是計算UITableView高度的代碼:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
C2 *cell = (C2 *)self.prototypeCell;
cell.t.text = [self.tableData objectAtIndex:indexPath.row];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
CGSize textViewSize = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)];
CGFloat h = size.height + textViewSize.height;
h = h > 89 ? h : 89; //89是圖片顯示的最低高度, 見xib
NSLog(@"h=%f", h);
return 1 + h;
}
在這兒我們是通過sizeThatFits:計算的UITextView的高度,然後加上systemLayoutSizeFittingSize:返回的高度。為什麼要這樣呢? 因為UITextView內容的高度不會影響systemLayoutSizeFittingSize計算。
下麵是UITextView的實例:
此圖中距頂的約束是10, 距底的約束8, 距左邊約束是87,距右邊的約束是13, 那麼systemLayoutSizeFittingSize:返回的CGSize為height等於19, size等於100. 它UITextView的frame是不影響systemLayoutSizeFittingSize:的計算。所以,我們需要加上textViewSize.height。
Manual Layout with UILabel
按照前麵介紹的,我們需要創建C3.xib, C3類, T3ViewController類,Main.storyboard中拖入UITableViewController,並分別建立關聯。 為了簡單,C3.xib中我就不加padding之類的了,如圖:
然後添加如下的計算代碼:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
C3 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C3"];
cell.t.text = [self.tableData objectAtIndex:indexPath.row];
[cell.t sizeToFit];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
C3 *cell = (C3 *)self.prototypeCell;
NSString *str = [self.tableData objectAtIndex:indexPath.row];
cell.t.text = str;
CGSize s = [str calculateSize:CGSizeMake(cell.t.frame.size.width, FLT_MAX) font:cell.t.font];
CGFloat defaultHeight = cell.contentView.frame.size.height;
CGFloat height = s.height > defaultHeight ? s.height : defaultHeight;
NSLog(@"h=%f", height);
return 1 + height;
}
這裏,用到了一個NSString的Cagetory方法,代碼如下:
- (CGSize)calculateSize:(CGSize)size font:(UIFont *)font
{
CGSize expectedLabelSize = CGSizeZero;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle.copy};
expectedLabelSize = [self boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;
}
else {
expectedLabelSize = [self sizeWithFont:font
constrainedToSize:size
lineBreakMode:NSLineBreakByWordWrapping];
}
return CGSizeMake(ceil(expectedLabelSize.width), ceil(expectedLabelSize.height));
}
Manual Layout with UITextView
按照前麵介紹的,我們需要創建C4.xib, C4類, T4ViewController類,Main.storyboard中拖入UITableViewController,並分別建立關聯。 為了簡單,C4.xib中我就不加padding之類的了,如圖:
相關代碼如下:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
C4 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C4"];
cell.t.text = [self.tableData objectAtIndex:indexPath.row];
[cell.t sizeToFit];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
C4 *cell = (C4 *)self.prototypeCell;
NSString *str = [self.tableData objectAtIndex:indexPath.row];
cell.t.text = str;
CGSize s = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)];
CGFloat defaultHeight = cell.contentView.frame.size.height;
CGFloat height = s.height > defaultHeight ? s.height : defaultHeight;
return 1 + height;
}
隨UITextView高度改變Cell高度
當UITextView內容改變的時候,計算自身高度,然後通知UITableView更新,這樣就會觸發UITableViewCell高度重新計算,從而改變Cell的高度。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
C5 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C5"];
cell.t.text = @"123";
cell.t.delegate = self;
return cell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
C5 *cell = (C5 *)self.prototypeCell;
cell.t.text = self.updatedStr;
CGSize s = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)];
CGFloat defaultHeight = cell.contentView.frame.size.height;
CGFloat height = s.height > defaultHeight ? s.height : defaultHeight;
return 1 + height;
}
#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([text isEqualToString:@"\n"]) {
NSLog(@"h=%f", textView.contentSize.height);
}
return YES;
}
- (void)textViewDidChange:(UITextView *)textView {
self.updatedStr = textView.text;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
關於UITableViewCell使用自動布局的優化可以查看下麵的介紹:
優化UITableViewCell高度計算。
StackView
UIStackView是iOS9新引入的控件,它支持垂直和水平排列多個子視圖(SubView)。例如:水平放置三個按鈕,等寬,並且按鈕間的間隙為10,如果自己實現會比較麻煩,而使用UIStackView則很容易實現。UIStackView目前隻支持iOS9+版本,如果要在iOS 7版本上使用UIStackView,可以使用下麵兩個第三方庫:OAStackView和TZStackView。其中:
- OAStackView,基於OC的StackView庫,支持iOS7+以上的係統,同時支持代碼和IB視圖。
- TZStackView,基於Swift的StackView庫,同樣支持iOS7+以上的係統,但是不支持storyboard。
OAStackView實現子視圖等分
案例1
例如,下麵的例子是使用OAStackView實現視圖等分的例子。
相關代碼如下:
UILabel *l1 = [[UILabel alloc] init];
l1.text = @"Label 1";
UILabel *l2 = [[UILabel alloc] init];
l2.text = @"Label 2";
OAStackView *stackView = [[OAStackView alloc] initWithArrangedSubviews:@[l1, l2]];
stackView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:stackView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-30-[stackView]"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(stackView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[stackView]"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(stackView)]];
上麵的例子實現了兩個按鈕的垂直排列。如果想要水平排列,修改stackView.axis值為UILayoutConstraintAxisHorizontal即可。需要注意,因為不是用IB創建的View,所以要設定View的translatesAutoresizingMaskIntoConstraints屬性為NO,否則排列屬性不生效。當非IB創建時,屬性默認為YES;當IB創建View時,屬性默認為NO。
案例2
在水平方向上放4張圖片,圖片等分。
1,首先在頁麵上拖拽1個imageView,將它的寬高都設置成50。
2,然後再添加三個imageView,將界麵上的四個ImageView設置等寬等高。
3,然後再將他們加入到Stack View中,設置Stack View 的distribution屬性為 Equal Spacing(等間距)。
StackView屬性
在理解StackView時,有幾個屬性需要理解:
Axis: 這個屬性是改變UIStackView中的排布方式的屬性,其中有水平排布與垂直排布
Alignment:這個屬性是其中子視圖的位置擺布方式默認是填充擺布
Fill:子視圖填充他所在的位置(默認)
Leading:子視圖頭部對齊
Center:子視圖居中對齊
Trailing:子視圖尾部對齊
Distribution:子視圖的大小
Fill:子視圖填充整個UIStackView
Fill Equally:子視圖填充空白區域並等分
Fill Proportionally:按照目前相對位置進行填充
Equal Spacing:等間距
Spacing設置子視圖之間的間距大小
Baseline Relative:如果設置子視圖間距的大小為基線到下一個視圖的頭部
最後更新:2017-09-05 10:03:35