一切的約束規則都遵照同一套固定的構造,而且都有相關的優先級:
view1.attribute(relation)view2.attribute*multiplier + constant
上述等式的每個局部都與NSLayoutConstraint對象的屬性絕對應,他們辨別是priority、firstItem。firstAttribute、relation、secondItem、secondAttribute、multiplier與constant。經過這些屬性,我們很容易比對兩條約束規則。
UIView會把約束規則當成NSLayoutConstraint對象來保管或許移除。即使兩條規則所描繪的含義相反,只需其內存地位不同,他們就是不相等的。假如完成了規則之間的比照功用,那麼就必需將規則專門寄存的變量外面了,而是可以經過順序嗎隨時添加並移除它們。
上面寫了三個辦法。constraint:matches:辦法會比擬兩條約束規則的相關屬性,以此判別兩者能否相反。請留意,在比擬的時分,我們只思索等式自身,而不思索優先級,由於兩條約束規則只需描繪的是同一套約束辦法,那麼無論開發者給其指定何種優先級,他們實踐上都是等效的。
還有兩個辦法辨別叫作constraintMatchingConstraint:及removeMatchingConstraint:,前者在視圖裡查找與開發者所給規則相婚配的首條約束規則,然後者則把此約束規則從視圖中移除。
上面會把某條約束規則從視圖裡刪除,並換上一條新約束規則,這樣的話,此視圖就會從下級視圖的頂部跑到下級視圖的底部了。關於這個復雜的狀況來說,我們完全可以把約束規則保管到實例變量中,並在稍後將其從視圖裡移除。不過,在需求管理多條約束規則或靜態移除某約束規則的時分,這種相似約束規則的查詢與移除功用還是十分有用的。
- (BOOL)constraint:(NSLayoutConstraint *)constraint1 matches:(NSLayoutConstraint *)constraint2 { if (constraint1.firstItem != constraint2.firstItem) { return NO; } if (constraint1.secondItem != constraint2.secondItem) { return NO; } if (constraint1.firstAttribute != constraint2.firstAttribute) { return NO; } if (constraint1.secondAttribute != constraint2.secondAttribute) { return NO; } if (constraint1.relation != constraint2.relation) { return NO; } if (constraint1.multiplier != constraint2.multiplier) { return NO; } if (constraint1.constant != constraint2.constant) { return NO; } return YES; } - (NSLayoutConstraint *)constrainMatchingConstraint:(NSLayoutConstraint *)aConstraint { for (NSLayoutConstraint *constraint in self.constraints) { if ([self constraint:constraint matches:aConstraint]) { return constraint; } } for (NSLayoutConstraint *constraint in self.superview.constraints) { if ([self constraint:constraint matches:aConstraint]) { return constraint; } } return nil; } - (void)removeMatchingConstraint:(NSLayoutConstraint *)aConstraint { NSLayoutConstraint *match = [self constrainMatchingConstraint:aConstraint]; if (match) { [self removeConstraint:match]; [self.superview removeConstraint:match]; } }
autoresizing可以指順序代碼裡用到的一些相關標志,例如UIViewAutoresizingFlexibleWidth等。假如要經過這些手腕來指定視圖的自動調整行為,那麼在定義約束規則的時分,就不應該再援用該視圖了。
運用約束規則之前,應該先禁用視圖中的有關屬性,該屬性會把觸及自動調整功用的掩碼自行轉換為約束規則。假如啟用該屬性,那麼視圖會經過與自動調整功用有關的掩碼來參與到Auto Layout零碎外面,要是禁用它,開發者就需求自己添加約束規則。
這個與約束規則有關的屬性叫作translatesAutoresizingMaskIntoConstraints。假如將其設為NO,我們就可以擔心的向視圖中添加自己的約束規則,而不用擔憂它會與零碎自動生成的規則相抵觸。這一點十分重要。若是尚未禁用該屬性就直接開端添加約束,則能夠會引發抵觸。由自動調整功用所生成的那些規則無法與自己編寫的這些規則戰爭共處。
2、令視圖呈現在下級視圖范圍內
上面第一個辦法叫作constrainWithinSuperviewBounds,它會把某視圖完全放在其下級視圖的范圍內。該辦法創立了四條約束規則來保證這一點。其中一條規則要求視圖的右邊界不許與下級視圖的右邊界對齊,或位於其右側,另一條規則要求本視圖的頂端必需與下級視圖的頂端對齊,或位於其下方,其他兩條也與之相似。
之所以要創立這個辦法,是由於在約束規則比擬寬松的零碎中,視圖的原點完全有能夠是負值,從而招致自己呈現在屏幕之外而不為用戶所見。該辦法的根本意思是:在排布這個子視圖的地位時,請思索到(0,0)原點與下級視圖的尺寸,而不要把它排布到下級視圖裡面去。
在日常開發中,我們不一定非要設置這套約束規則。但在剛開端接觸約束並想經過代碼來研討它的時分,這種約束規則就顯得十分有用了。他可以確保本視圖不會越出下級視圖的范圍,從而令我們可以測試其他各項約束規則,並察看這些規則之間的關系。
此外,在熟習了約束零碎之後,我們能夠還想給順序添加一些調試用的反應代碼,以便在主視圖加載終了並運用了約束規則之後,可以知道我們自己的視圖位於何處。比如說,可以把上面這個for循環添加到viewDidAppear:辦法中:
- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; for (UIView *view in self.view.subviews) { NSLog(@"View (%d) location:%@",[self.view.subviews indexOfObject:view],NSStringFromCGRect(view.frame)); } }
上面第二個辦法叫作constrainSize:,他會把視圖的尺寸固定為開發者所指定的CGSize。在定義約束規則的時分,這是一種常常會用到的操作。我們不能再像原來那樣設定視圖的框架。另外也請記得:這些約束規則都只是一種懇求,未必總是與最終的規劃完全相符。假設沒有合理的設計約束規則,那麼寬度為100個點的文本框在最終的順序界面裡其寬度能夠會變成107個點,或更糟。
我們可以在約束規則中針對某視圖來指定其寬度或高度,但是這兩條規則所要用到的寬度值和高度值是不能預判的,由於constrainSize:辦法要運用與各種視圖才行。於是,我們把這兩個值先放在metrics字典裡,然後再傳給constraintsWithVisualFormat。所謂目標,其實就是約束規則裡所用到的一些數值變量。
這兩條規則所用的兩個目標辨別叫作theHeight和theWidth,這些稱號完全可以隨機選取,開發者應該把字符串作為鍵,放在傳給metrics:參數的字典裡。在調用創立約束規則的那個辦法時,把這個字典傳出來。目標裡的每個鍵必需呈現在這份字典中,而且它所對應的值必需是個NSNumber。
辦法裡的兩條約束規則設定了我們所希冀的視圖的寬度及視圖的高度。兩個格式字符串辨別通知約束零碎以後這個視圖在橫軸和縱軸上的尺寸應該是多少。
第三個辦法叫作constrainPosition:,他會構建兩條約束規則,用於確定本視圖的原點在下級視圖裡的地位。
我們要用約束規則把上述標簽和其下級視圖聯絡起來,但是在添加規則之前,必需先把標簽控件作為子視圖放到下級視圖裡。在其上添加約束規則的那個視圖和規則中所提到的視圖必需位於同一個視圖體系中,甚至會令順序在運轉的時分解體。所以,我們有時能夠要稍稍調整一下代碼順序,比如說,下面這個辦法就會在添加約束規則之前,先把標簽放在下級視圖中。
無論按何種順序來調整視圖的生成代碼,我們都要遵照下列步驟:首先創立視圖,然後將其添加到下級視圖裡,接上去禁用translatesAutoresizingMaskIntoConstraints,最後運用必要的約束規則。
- (void)constrainWithinSuperviewBounds { if (!self.superview) { return; } [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.superview attribute:NSLayoutAttributeLeft multiplier:1 constant:0]]; [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.superview attribute:NSLayoutAttributeTop multiplier:1 constant:0]]; [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.superview attribute:NSLayoutAttributeRight multiplier:1 constant:0]]; [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.superview attribute:NSLayoutAttributeBottom multiplier:1 constant:0]]; } - (void)constrainSize:(CGSize)aSize { NSMutableArray *array = [NSMutableArray array]; [array addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[self(theWith@750)]" options:0 metrics:@{@"theWidth":@(aSize.width)} views:NSDictionaryOfVariableBindings(self)]]; [array addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[self(theHeight@750)]" options:0 metrics:@{@"theWidth":@(aSize.height)} views:NSDictionaryOfVariableBindings(self)]]; [self addConstraints:array]; } - (void)constrainPosition:(CGPoint)aPoint { if (!self.superview) { return; } NSMutableArray *array = [NSMutableArray array]; [array addObject:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeLeft multiplier:1 constant:0]]; [array addObject:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeTop multiplier:1 constant:0]]; [self.superview addConstraints:array]; }
假如想把兩個視圖居中對齊,可以將某視圖的中心點屬性同其容器的對應屬性關聯起來。有兩個辦法可以取得某視圖的下級視圖,並在這兩個視圖的中心點之間施加同等關系。
上面是兩條復雜的准繩:
1、創立約束規則的時分,假如規則所觸及的某個視圖是另一個視圖的下級視圖,那麼就把規則添加到這個下級視圖裡。
2、假如用格式化字符串來創立約束規則,而字符串外面又包括表示下級視圖的豎線,那麼就把規則添加到對應的下級視圖裡。
- (void)centerHorizontallyInSuperview { if (!self.superview) { return; } [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; } - (void)centerVerticallyInSuperview { if (!self.superview) { return; } [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]]; }
在約束規則中運用縮小倍數,也就是等式y=m*x+b中的m,即可設置視圖的寬高比。
為了切換寬高比,這個方案會把設置好的約束規則保管到NSLayoutConstraint型的aspectConstraint變量裡。用戶每次把比例從16:9切換到4:3或是切換回去時,這個處理方案都會將前一條約束規則從視圖裡移除,然後創立新規則並將其保管起來。創立新規則的時分要設置適當的縮小倍數,創立好之後,還要將其添加到視圖外面。
我們既想令視圖的寬度和高度可以靈敏變化,又想使其堅持一定的尺寸,所以,這條處理方案的createLabel辦法需求做兩件事。首先,要用謂詞來限定寬度及高度,它懇求零碎吧寬度和高度都至多設為300個點。其次,要給這一懇求指定優先級。我們把優先級設定的比擬高,但是並沒有設定成非滿足不可,這樣就給約束零碎留下了持續調整規劃的余地。做完這兩件事之後,我們就完成了一套可以實時調整寬高比的順序界面,而且順序還可以在運轉的時分靜態改動其規劃。
限定寬高比的約束規則也可以用來堅持某張圖像的橫縱比例。視圖的contentMode能夠無法完全精確的設定圖像的橫縱比。我們可以經過UIImage的size屬性得知圖像尺寸,然後用其寬度除以高度,並據此創立出契合橫縱比的約束規則來。
- (UILabel *)createLabelWithTitle:(NSString *)title onParent:(UIView *)parentView { UILabel *label = [[UILabel alloc] init]; label.textAlignment = NSTextAlignmentCenter; label.backgroundColor = [UIColor greenColor]; [parentView addSubview:label]; label.translatesAutoresizingMaskIntoConstraints = NO; [label constrainWithinSuperviewBounds]; [label addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[label(>=theWidth@750)]" options:0 metrics:@{@"theWidth":@300} views:NSDictionaryOfVariableBindings(label)]]; label addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[label(>=theHeight@750)]" options:0 metrics:@{@"theHeight":@300} views:NSDictionaryOfVariableBindings(label)]; [label centerInSuperview]; return label; } - (void)toggleaspectRatio { if (aspectConstraint) { [self.view removeConstraint:aspectConstraint]; } if (useFourToThree) { aspectConstraint = [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:view1 attribute:NSLayoutAttributeHeight multiplier:(4.0/3.0) constant:0]; }else{ aspectConstraint = [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:view1 attribute:NSLayoutAttributeHeight multiplier:(16.0/9.0) constant:0]; } [self.view addConstraint:aspectConstraint]; useFourToThree = !useFourToThree; }
設備屏幕的幾何特征也會影響界面的規劃方式。比如說,當設備處在橫屏形態的時分,垂直方向上就沒有太多的空間可以排布內容。
為了順應這兩種規劃,我們必需依據屏幕方向的變卦狀況來修正約束規則,偏重新排版。updateViewControllerConstraints辦法可以按照以後的屏幕方向來刷新約束規則,它會把已有的規則全部移除,並設置新的約束。我們應該在willAnimateRotationToInterfaceOrientation:duration:外面調用此辦法。這樣所發生的效果就可以和界面裡的其他動畫效果平滑的交融起來。另外要留意,零碎是在把視圖控制器的interfaceOrientation屬性設置好之後,再去調用willAnimateRotationToInterfaceOrientation的,而不是像willRotateTointerfaceOrientation:duration:辦法那樣在該屬性變卦之前回調。這樣的話,updateViewControllerConstraint辦法所查找到的界面方向自身曾經是正確的界面方向了,我們可以依據此生成適當的約束規則。
- (void)updateViewControllerConstraints
{
[self.view removeConstraints:self.view.constraints];
NSDictionary *bindings = NSDictionaryOfVariableBindings(imageView,titleLabel,artistLabel,button);
if (UIDeviceOrientationIsPortrait(self.interfaceOrientation) || (self.interfaceOrientation == UIDeviceOrientationUnknown)) {
for (UIView *view in @[imageView,titleLabel,artistLabel,button]) {
CENTER_VIEW_H(self.view,view);
}
CONSTRAIN_VIEWS(self.view,@"V:|-80-[imageView]-30-\[titleLabel(>=0)]-[artisLabel]-15-[button]-(>=0)-|",bindings);
}else{
CENTER_VIEW_V(self.view,imageView);
CONSTRAIN(self.view,imageView,@"H:|-[imageView]");
CONSTRAIN(self.view,titleLabel,@"H:[titleLabel]-15-|");
CONSTRAIN(self.view,artistLabel,@"H:[artistLabel]-15-|");
CONSTRAIN(self.view,button,@"H:[button]-15-|");
CONSTRAIN_VIEWS(self.view,@"V:|-(>=0)-[titleLabel(>=0)]\-[artistLabel]-15-[button]-|",bindings);
CONSTRAIN_VIEWS(self.view,@"H:[imageView]-(>=0)-[titleLabel]",bindings);
}
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
[self updateViewControllerConstraints];
}
【ios 約束(六)】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!