在平時(shí)開(kāi)發(fā)中ScrollView扮演非常重要的角色贮缅,經(jīng)常需要給ScrollView添加一些ContentViews, 并能上下左右滑動(dòng)篇梭, 但是當(dāng)你添加完這些ContentViews,并設(shè)置好約束之后,驚訝的發(fā)現(xiàn)ScrollView上沒(méi)有任何內(nèi)容或ScrollView并不能滑動(dòng)。一陣驚慌中,瘋狂開(kāi)啟NSLog模式玩敏,茫然發(fā)現(xiàn)frame為0,contentSize為0质礼,什么情況旺聚?
導(dǎo)致這種現(xiàn)象的原因不是我們的約束設(shè)置有問(wèn)題,而是在AutoLayout模式下ScrollView計(jì)算ContentSize的機(jī)制不一樣的眶蕉,ContentSize是依據(jù)ContentViews的寬和高自動(dòng)計(jì)算出來(lái)的砰粹,不需要我們手動(dòng)設(shè)置。在ViewDidLoad之后造挽,系統(tǒng)會(huì)根據(jù)ContentViews情況重新算出ScrollView的ContentSize碱璃。
我們?cè)谔砑覵crollView時(shí),只是給出了到屏幕各個(gè)邊的約束為0饭入,卻沒(méi)有給出具體的寬度和高度嵌器, 所以無(wú)法確定ContentSize大小,自然不能滑動(dòng)谐丢。建議在ScrollView上先添加一個(gè)UIView爽航,UIView的大小和ScrollView一樣,然后在UIView上添加ContentViews, 這樣系統(tǒng)就會(huì)準(zhǔn)確的算出ScrollView的ContentSize, 保證ScrollView能滑動(dòng)起來(lái)乾忱。
NSLayoutConstraint有兩個(gè)添加constraint的方法:
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *, id> *)views;
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
WithVisualFormat方法比WithItem更加形象化讥珍、簡(jiǎn)潔化,建議多使用可視化方法饭耳。
在給ScrollView添加子視圖之前串述,建議先弄清楚ScrollView上一共有多少個(gè)子視圖执解,并使用可視化方法一并寫(xiě)出來(lái)寞肖,告訴系統(tǒng)我需要添加這些視圖纲酗,等之后這些視圖的尺寸確定下來(lái)后,你就可以計(jì)算contentSize了新蟆。否則觅赊,系統(tǒng)不知道你一共有多少個(gè)子視圖,也不知道第一個(gè)和最后一個(gè)子視圖是誰(shuí)琼稻,這樣就難以計(jì)算contentSize吮螺。
我現(xiàn)在需要在ScrollView上添加一個(gè)UIImageView,一個(gè)UIView, 一個(gè)UILabel帕翻。
在屏幕中位置如下:
ScrollView:
scrollView大小為屏幕大小鸠补,背景色white
ContentView:
contentView大小同scrollView大小,背后色red
UIImageView:
. top 距離底部60,
. left 距離左邊5
. right 距離右邊5
. height 高度100
背景色green
UIView:
. top 距離上面10
. left 距離左邊10
. right 距離右邊10
. height 高度80
背景色purple
UILabel:
. top 距離上面8
. left 距離左邊8
. right 距離右邊8
. height 高度根據(jù)文字內(nèi)容自動(dòng)調(diào)整
背景色yellow
效果圖如下:
給ScrollView和ContentView添加約束的代碼如下:
self.automaticallyAdjustsScrollViewInsets = NO;
UIScrollView *scView = [[UIScrollView alloc] init];
scView.backgroundColor = [UIColor whiteColor];
scView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(0)-[scView]-(0)-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(scView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(0)-[scView]-(0)-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(scView)]];
UIView *contentView = [[UIView alloc] init];
contentView.backgroundColor = [UIColor redColor];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
[scView addSubview:contentView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(0)-[contentView]-(0)-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(0)-[contentView]-(0)-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(contentView)]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];
注意:由于ScrollView可以垂直和水平滑動(dòng)嘀掸,所以我們指定ContentView的寬度為屏幕寬度后紫岩,scrollView就只能垂直滑動(dòng)了。
在添加子試圖之前需要一次性將所有的子視圖列舉出來(lái)睬塌,并設(shè)置好子視圖之間的上下間距
UIImageView *imageView = [[UIImageView alloc] init];
imageView.backgroundColor = [UIColor greenColor];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addSubview:imageView];
UIView *boxView = [[UIView alloc] init];
boxView.translatesAutoresizingMaskIntoConstraints = NO;
boxView.backgroundColor = [UIColor purpleColor];
[contentView addSubview:boxView];
UILabel *label = [[UILabel alloc] init];
label.backgroundColor = [UIColor yellowColor];
label.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addSubview:label];
label.text = @"起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字起個(gè)很長(zhǎng)很長(zhǎng)的名字";
label.numberOfLines = 0;
[contentView addSubview:label];
NSDictionary *tmpViewsDictionary = @{@"imageView":imageView,
@"boxView":boxView,
@"label":label,
};
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(60)-[imageView(100)]-(10)-[boxView(80)]-(8)-[label]-(8)-|" options:0 metrics:nil views:tmpViewsDictionary]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(5)-[imageView]-(5)-|" options:0 metrics:nil views:tmpViewsDictionary]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(10)-[boxView]-(10)-|" options:0 metrics:nil views:tmpViewsDictionary]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(8)-[label]-(8)-|" options:0 metrics:nil views:tmpViewsDictionary]];
注意:需要時(shí)刻提醒自己準(zhǔn)確的指出每個(gè)子視圖的寬和高泉蝌,這樣ScrollView才能算出ContentSize。UILabel由于會(huì)根據(jù)text內(nèi)容自動(dòng)算出高度揩晴,所以不需要明確列出label的高度勋陪,而UIView如果不給出其高度的話,是無(wú)法正確顯示的硫兰。
最后如果我們將boxView的高度改的足夠高诅愚,如800,就可以看見(jiàn)ScrollView能像預(yù)期的那樣滑動(dòng)起來(lái)了
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(60)-[imageView(100)]-(10)-[boxView(800)]-(8)-[label]-(8)-|" options:0 metrics:nil views:tmpViewsDictionary]];
最后吐槽下:NSLayoutConstraint真的很難用劫映。