[英]Auto Layout: portrait to landscape inconsistencies
我正在經歷“自動布局比努力更值得”階段。 我越來越好了,但是有些地方還是不會點擊,至少可以說令人沮喪
以下是有助於顯示我的位置的圖像以及它從模擬器返回的圖像:
中心“ ...”文本標簽位於中心的V&H。 所有4個按鈕在此標簽上都具有尾隨底部的空間限制。 每個按鈕之間都有一個試用空間約束。
當我在UI編輯器的外部邊緣上添加前導和尾隨空間約束時(上圖),我得到的結果如我所願(下圖)
然而,旋轉90度會導致4個按鈕壓扁並且不可見(在某些嘗試中4個按鈕彼此重疊)。
我已經嘗試了約束的所有不同組合,但是從兩個方向我都無法實現我想要的視圖。 這個問題可能會受到批評,但是老實說,這是獲得答案的唯一直接方法,甚至只是一些幫助。 我發現在線教程和示例僅說明了我必須使用哪些工具,而不必大膽嘗試UI元素的“比基本功能更多”的定位
這是我為NSLayoutConstraints編寫的自定義類別:
首先, NSLayoutConstraint+ConstructorAdditions.h
文件:
// NSLayoutConstraint+ConstructorAdditions.h
//
// The methods in this category can be used to create NSLayoutConstraints programmatically
// without taking the NSLayoutConstraints constructors.
// Note: view1 and view2 must be in the same subview hierarchy. If not, adding NSLayoutConstraints using these methods won't have no effect.
//
// Example for the right order of method calls creating Constraints:
//
// UIView* mySubview = [UIView alloc] init];
// [self.contentView addSubview: mySubview];
// [self.contentView addConstraint: [self height: 10.0 forView: mySubview]];
#import <UIKit/UIKit.h>
@interface NSLayoutConstraint (ConstructorAdditions)
// returns constraints that align view to top, leading, bottom and trailing edges of the superview according to the assigned padding value.
// the constraints in the returned array are (from index 0 to 3) in this order:
// leading, top, trailing, bottom
+ (NSArray*) makeView: (UIView*) view
fillBoundsOfSuperview: (UIView*) superview
verticalPadding: (CGFloat) vPadding
horizontalPadding: (CGFloat) hPadding;
// align edges of two views
// if one view is the superview, put it at the second position in the array
+ (NSLayoutConstraint*) alignEdge: (NSLayoutAttribute) edge
ofViews: (NSArray*) /*UIView*/ views
relation: (NSLayoutRelation) relation
spacing: (CGFloat) spacing;
// vertical spcacing between views
// the method assumes, that the first view in the array is above the second view
+ (NSLayoutConstraint*) verticalSpacing: (CGFloat) spacing
ofViews: (NSArray*) /*UIView*/ views
flexible: (BOOL) flexible
inMaximum: (BOOL) inMax;
// horizontal spacing between views
// the method assumes, that the first view in the array is left of the second view
+ (NSLayoutConstraint*) horizontalSpacing: (CGFloat) spacing
betweenViews: (NSArray*) /*UIView*/ views
flexible: (BOOL) flexible
inMaximum: (BOOL) inMax;
// vertical spcacing between views
// the method assumes, that the first view in the array is above the second view
+ (NSLayoutConstraint*) verticalSpacing: (CGFloat) spacing ofViews: (NSArray*) /*UIView*/ views;
// horizontal spacing between views
// the method assumes, that the first view in the array is left of the second view
+ (NSLayoutConstraint*) horizontalSpacing: (CGFloat) spacing betweenViews: (NSArray*) /*UIView*/ views;
// height constraints
+ (NSArray*) equalHeightOfViews: (NSArray*) views
toView: (UIView*) view
distance: (CGFloat) distance
relation: (NSLayoutRelation) relation
multiplier: (CGFloat) multiplier;
// fix heights
+ (NSLayoutConstraint*) height: (CGFloat) height forView: (UIView*) view;
+ (NSLayoutConstraint*) maxHeight: (CGFloat) height
forView: (UIView*) view;
+ (NSLayoutConstraint*) minHeight: (CGFloat) height
forView: (UIView*) view;
// width constraints
+ (NSArray*) equalWidthOfViews: (NSArray*) views
toView: (UIView*) view
distance: (CGFloat) distance
relation: (NSLayoutRelation) relation
multiplier: (CGFloat) multiplier;
// fix widths
+ (NSLayoutConstraint*) width: (CGFloat) width forView: (UIView*) view;
+ (NSLayoutConstraint*) maxWidth: (CGFloat) width
forView: (UIView*) view;
+ (NSLayoutConstraint*) minWidth: (CGFloat) width
forView: (UIView*) view;
// center vertically
+ (NSLayoutConstraint*) centerView: (UIView*) view
verticallyInContainer: (UIView*) container;
// center horizontally
+ (NSLayoutConstraint*) centerView: (UIView*) view
hoizontallyInContainer: (UIView*) container;
@end
這里是NSLayoutConstraint+ConstructorAdditions.m
文件:
#import "NSLayoutConstraint+ConstructorAdditions.h"
@implementation NSLayoutConstraint (ConstructorAdditions)
+ (NSArray*) makeView: (UIView*) view fillBoundsOfSuperview: (UIView*) superview verticalPadding: (CGFloat) vPadding horizontalPadding: (CGFloat) hPadding
{
/* padding must be > 0 !*/
hPadding = hPadding < 0 ? 0 : hPadding;
vPadding = vPadding < 0 ? 0 : vPadding;
NSLayoutConstraint* top = [NSLayoutConstraint alignEdge: NSLayoutAttributeTop ofViews: @[view, superview] relation: NSLayoutRelationEqual spacing: vPadding];
NSLayoutConstraint* leading = [NSLayoutConstraint alignEdge: NSLayoutAttributeLeading ofViews: @[view, superview] relation: NSLayoutRelationEqual spacing: hPadding];
NSLayoutConstraint* trailing = [NSLayoutConstraint alignEdge: NSLayoutAttributeTrailing ofViews: @[view, superview] relation: NSLayoutRelationEqual spacing: hPadding];
NSLayoutConstraint* bottom = [NSLayoutConstraint alignEdge: NSLayoutAttributeBottom ofViews: @[view, superview] relation: NSLayoutRelationEqual spacing: vPadding];
//NSLayoutConstraint* width = [NSLayoutConstraint equalWidthOfViews: @[view, superview] distance: -2*hPadding relation: NSLayoutRelationEqual multiplier: 1.0][0];
//NSLayoutConstraint* height = [NSLayoutConstraint equalHeightOfViews: @[view, superview] distance: -2*vPadding relation: NSLayoutRelationEqual multiplier: 1.0][0];
return @[leading, top, trailing, bottom];
}
// align edges of two views
// if one view is the superview, put it at the second position in the array
+ (NSLayoutConstraint*) alignEdge: (NSLayoutAttribute) edge
ofViews: (NSArray*) /*UIView*/ views
relation: (NSLayoutRelation) relation
spacing: (CGFloat) spacing {
NSLayoutConstraint* constraint = nil;
if (views.count == 2) {
if (edge == NSLayoutAttributeBaseline || edge == NSLayoutAttributeTrailing || edge == NSLayoutAttributeBottom || edge == NSLayoutAttributeRight) {
spacing = -spacing;
}
constraint = [NSLayoutConstraint constraintWithItem: [views objectAtIndex: 0]
attribute: edge
relatedBy: relation
toItem: [views objectAtIndex: 1]
attribute: edge
multiplier: 1.0
constant: spacing];
}
return constraint;
}
// vertical spcacing between views
// the method assumes, that the first view in the array is above the second view
+ (NSLayoutConstraint*) verticalSpacing: (CGFloat) spacing
ofViews: (NSArray*) /*UIView*/ views
flexible: (BOOL) flexible
inMaximum: (BOOL) inMax {
NSLayoutConstraint* constraint = nil;
NSLayoutRelation relation = flexible ? (inMax ? NSLayoutRelationGreaterThanOrEqual : NSLayoutRelationLessThanOrEqual) : NSLayoutRelationEqual;
if (views.count == 2) {
constraint = [NSLayoutConstraint constraintWithItem: [views objectAtIndex: 0]
attribute: NSLayoutAttributeBottom
relatedBy: relation
toItem: [views objectAtIndex: 1]
attribute: NSLayoutAttributeTop
multiplier: 1.0
constant: -spacing];
}
return constraint;
}
// horizontal spacing between views
// the method assumes, that the first view in the array is left of the second view
+ (NSLayoutConstraint*) horizontalSpacing: (CGFloat) spacing
betweenViews: (NSArray*) /*UIView*/ views
flexible: (BOOL) flexible
inMaximum: (BOOL) inMax {
NSLayoutConstraint* constraint = nil;
NSLayoutRelation relation = flexible ? (inMax ? NSLayoutRelationGreaterThanOrEqual : NSLayoutRelationLessThanOrEqual) : NSLayoutRelationEqual;
if (views.count == 2) {
constraint = [NSLayoutConstraint constraintWithItem: [views objectAtIndex: 0]
attribute: NSLayoutAttributeTrailing
relatedBy: relation
toItem: [views objectAtIndex: 1]
attribute: NSLayoutAttributeLeading
multiplier: 1.0
constant: -spacing];
}
return constraint;
}
// vertical spcacing between views
// the method assumes, that the first view in the array is above the second view
+ (NSLayoutConstraint*) verticalSpacing: (CGFloat) spacing ofViews: (NSArray*) /*UIView*/ views
{
NSLayoutConstraint* verticalSpacing = nil;
if (views.count == 2) {
verticalSpacing = [NSLayoutConstraint constraintWithItem: [views objectAtIndex: 0] attribute: NSLayoutAttributeBottom relatedBy: NSLayoutRelationEqual toItem: [views objectAtIndex: 1] attribute: NSLayoutAttributeTop multiplier: 1.0 constant: -spacing];
}
return verticalSpacing;
}
// horizontal spacing between views
// the method assumes, that the first view in the array is left of the second view
+ (NSLayoutConstraint*) horizontalSpacing: (CGFloat) spacing
betweenViews: (NSArray*) /*UIView*/ views
{
NSLayoutConstraint* horizontalSpacing = nil;
if (views.count == 2) {
horizontalSpacing = [NSLayoutConstraint constraintWithItem: [views objectAtIndex: 0] attribute: NSLayoutAttributeTrailing relatedBy: NSLayoutRelationEqual toItem: [views objectAtIndex: 1] attribute: NSLayoutAttributeLeading multiplier: 1.0 constant: -spacing];
}
return horizontalSpacing;
}
+ (NSArray*) equalHeightOfViews: (NSArray*) views
toView: (UIView*) view
distance: (CGFloat) distance
relation: (NSLayoutRelation) relation
multiplier: (CGFloat) multiplier {
NSMutableArray* constraints = [[NSMutableArray alloc] initWithCapacity: views.count];
for (UIView* aView in views) {
[constraints addObject: [NSLayoutConstraint constraintWithItem: aView
attribute: NSLayoutAttributeHeight
relatedBy: relation
toItem: view
attribute: NSLayoutAttributeHeight
multiplier: multiplier
constant: distance]];
}
return constraints;
}
+ (NSLayoutConstraint*) height: (CGFloat) height forView: (UIView*) view {
NSLayoutConstraint* constraint = nil;
constraint = [NSLayoutConstraint constraintWithItem: view
attribute: NSLayoutAttributeHeight
relatedBy: NSLayoutRelationEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
constant: height];
return constraint;
}
+ (NSLayoutConstraint*) maxHeight: (CGFloat) height
forView: (UIView*) view {
NSLayoutConstraint* constraint = [NSLayoutConstraint constraintWithItem: view
attribute: NSLayoutAttributeHeight
relatedBy: NSLayoutRelationLessThanOrEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
constant: height];
return constraint;
}
+ (NSLayoutConstraint*) minHeight: (CGFloat) height
forView: (UIView*) view {
NSLayoutConstraint* constraint = [NSLayoutConstraint constraintWithItem: view
attribute: NSLayoutAttributeHeight
relatedBy: NSLayoutRelationGreaterThanOrEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
constant: height];
return constraint;
}
+ (NSArray*) equalWidthOfViews: (NSArray*) views
toView: (UIView*) view
distance: (CGFloat) distance
relation: (NSLayoutRelation) relation
multiplier: (CGFloat) multiplier {
NSMutableArray* constraints = [[NSMutableArray alloc] initWithCapacity: views.count];
for (UIView* aView in views) {
[constraints addObject: [NSLayoutConstraint constraintWithItem: aView
attribute: NSLayoutAttributeWidth
relatedBy: relation
toItem: view
attribute: NSLayoutAttributeWidth
multiplier: multiplier
constant: distance]];
}
return constraints;
}
+ (NSLayoutConstraint*) width: (CGFloat) width forView: (UIView*) view {
NSLayoutConstraint* constraint = [NSLayoutConstraint constraintWithItem: view
attribute: NSLayoutAttributeWidth
relatedBy: NSLayoutRelationEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
constant: width];
return constraint;
}
+ (NSLayoutConstraint*) maxWidth: (CGFloat) width forView: (UIView*) view {
NSLayoutConstraint* constraint = [NSLayoutConstraint constraintWithItem: view
attribute: NSLayoutAttributeWidth
relatedBy: NSLayoutRelationLessThanOrEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
constant: width];
return constraint;
}
+ (NSLayoutConstraint*) minWidth: (CGFloat) width forView: (UIView*) view {
NSLayoutConstraint* constraint = [NSLayoutConstraint constraintWithItem: view
attribute: NSLayoutAttributeWidth
relatedBy: NSLayoutRelationGreaterThanOrEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
constant: width];
return constraint;
}
// center vertically
+ (NSLayoutConstraint*) centerView: (UIView*) view verticallyInContainer: (UIView*) container {
NSLayoutConstraint* centerY = [NSLayoutConstraint constraintWithItem: view
attribute: NSLayoutAttributeCenterY
relatedBy: NSLayoutRelationEqual
toItem: container
attribute: NSLayoutAttributeCenterY
multiplier: 1.0
constant: 0];
return centerY;
}
// center horizontally
+ (NSLayoutConstraint*) centerView: (UIView*) view hoizontallyInContainer: (UIView*) container {
NSLayoutConstraint* centerX = [NSLayoutConstraint constraintWithItem: view
attribute: NSLayoutAttributeCenterX
relatedBy: NSLayoutRelationEqual
toItem: container
attribute: NSLayoutAttributeCenterX
multiplier: 1.0
constant: 0];
return centerX;
}
@end
在您的初始圖片中,標簽之所以顯示為橙色的原因是因為您的約束存在歧義:具體來說,它們沒有任何內容告訴他們相對於x軸應位於何處。 它們在垂直方向上連接到中心,但是在水平方向上它們僅彼此連接,並且由於它們都沒有作為x軸錨點連接到邊緣或其他任何東西,因此存在歧義。
在第二張圖片中,您已通過將鏈條的兩側錨定到視圖的邊緣來解決此問題。 但是,在縱向模式中,這會擠壓它們,因為那些外部錨點正試圖保持在原位,並且它選擇擠壓標簽上的寬度約束。
解決此問題的方法如下:選擇一個標簽(不一定是全部標簽)並將其約束到容器的水平中心,在本例中為superview。
這將告知標簽相對於x軸的位置,同時還可以防止標簽被縱向視圖的側面擠壓。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.