xcode 如何使用 AutoLayout 将 UIButtons 放置在水平线上(环绕、左对齐)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19808739/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use AutoLayout to position UIButtons in horizontal lines (wrapping, left aligned)?
提问by thomers
I need to create a couple of UIButtons with various widths programmatically in my app (iOS 6.0 and above).
我需要在我的应用程序(iOS 6.0 及更高版本)中以编程方式创建几个不同宽度的 UIButton。
I want to display the buttons in a "wrap around" style: Starting from the left edge, each button should be positioned next to each other horizontally (in a defined order), and if a button does not fit in the current "line", it should start a new line on the left edge below the previous line.
我想以“环绕”样式显示按钮:从左边缘开始,每个按钮应水平放置(按定义的顺序),如果按钮不适合当前“行” ,它应该在上一行下方的左边缘开始一个新行。
Note: I don't want a table/grid, since the buttons have different widths, and I want to have one right next to each other.
注意:我不想要一个表格/网格,因为按钮的宽度不同,而且我想要一个并排放置。
I could manually calculate the frame of each button in my code, but should I use AutoLayout (with programmatically created NSLayoutConstraints) instead? How exactly would I need to set it up?
我可以手动计算代码中每个按钮的框架,但是我应该使用 AutoLayout(使用以编程方式创建的 NSLayoutConstraints)吗?我究竟需要如何设置它?
EDIT: After reading through Chapter 4 "Intermediate Auto Layout" of "iOS 6 by Tutorials"I am not sure whether using pure AutoLayout could implement this "wrap around" functionality I require.
编辑:在阅读“iOS 6 by Tutorials”的第 4 章“Intermediate Auto Layout”后,我不确定使用纯 AutoLayout 是否可以实现我需要的这种“环绕”功能。
采纳答案by thomers
My current solution looks like this: No AutoLayout, but manually setting the correct constraints for each case (first button, leftmost button in a new line, any other button).
我当前的解决方案是这样的:没有 AutoLayout,而是为每种情况手动设置正确的约束(第一个按钮,新行中最左边的按钮,任何其他按钮)。
(My guess is that setting the frame for each button directly would result in more readable code than using NSLayoutConstraints, anyway)
(我的猜测是,与使用 NSLayoutConstraints 相比,直接为每个按钮设置框架会导致代码更具可读性)
NSArray *texts = @[ @"A", @"Short", @"Button", @"Longer Button", @"Very Long Button", @"Short", @"More Button", @"Any Key"];
int indexOfLeftmostButtonOnCurrentLine = 0;
NSMutableArray *buttons = [[NSMutableArray alloc] init];
float runningWidth = 0.0f;
float maxWidth = 300.0f;
float horizontalSpaceBetweenButtons = 10.0f;
float verticalSpaceBetweenButtons = 10.0f;
for (int i=0; i<texts.count; i++) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:[texts objectAtIndex:i] forState:UIControlStateNormal];
[button sizeToFit];
button.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:button];
// check if first button or button would exceed maxWidth
if ((i == 0) || (runningWidth + button.frame.size.width > maxWidth)) {
// wrap around into next line
runningWidth = button.frame.size.width;
if (i== 0) {
// first button (top left)
// horizontal position: same as previous leftmost button (on line above)
NSLayoutConstraint *horizontalConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0f constant:horizontalSpaceBetweenButtons];
[self.view addConstraint:horizontalConstraint];
// vertical position:
NSLayoutConstraint *verticalConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0f constant:verticalSpaceBetweenButtons];
[self.view addConstraint:verticalConstraint];
} else {
// put it in new line
UIButton *previousLeftmostButton = [buttons objectAtIndex:indexOfLeftmostButtonOnCurrentLine];
// horizontal position: same as previous leftmost button (on line above)
NSLayoutConstraint *horizontalConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:previousLeftmostButton attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0.0f];
[self.view addConstraint:horizontalConstraint];
// vertical position:
NSLayoutConstraint *verticalConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:previousLeftmostButton attribute:NSLayoutAttributeBottom multiplier:1.0f constant:verticalSpaceBetweenButtons];
[self.view addConstraint:verticalConstraint];
indexOfLeftmostButtonOnCurrentLine = i;
}
} else {
// put it right from previous buttom
runningWidth += button.frame.size.width + horizontalSpaceBetweenButtons;
UIButton *previousButton = [buttons objectAtIndex:(i-1)];
// horizontal position: right from previous button
NSLayoutConstraint *horizontalConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:previousButton attribute:NSLayoutAttributeRight multiplier:1.0f constant:horizontalSpaceBetweenButtons];
[self.view addConstraint:horizontalConstraint];
// vertical position same as previous button
NSLayoutConstraint *verticalConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:previousButton attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f];
[self.view addConstraint:verticalConstraint];
}
[buttons addObject:button];
}
回答by Abizern
Instead of using Autolayout, you could just use a collection view which better options for you to lay out elements such as buttons.
您可以不使用自动布局,而是使用集合视图,它可以为您提供更好的选项来布局按钮等元素。
It is better able to handle layouts under rotation as well.
它也能更好地处理旋转布局。
回答by hsafarya
Here is the another example of how we can implement wrapping layout with auto layout:
这是我们如何使用自动布局实现环绕布局的另一个示例:
@interface SCHorizontalWrapView : UIView
@property(nonatomic)NSMutableArray *wrapConstrains;
@end
@implementation SCHorizontalWrapView {
CGFloat intrinsicHeight;
BOOL updateConstraintsCalled;
}
-(id)init {
self = [super init];
if (self) {
[UIView autoSetPriority:UILayoutPriorityDefaultHigh forConstraints:^{
[self autoSetContentCompressionResistancePriorityForAxis:ALAxisVertical];
[self autoSetContentCompressionResistancePriorityForAxis:ALAxisHorizontal];
[self autoSetContentCompressionResistancePriorityForAxis:ALAxisHorizontal];
[self autoSetContentCompressionResistancePriorityForAxis:ALAxisVertical];
}];
}
return self;
}
-(void)updateConstraints {
if (self.needsUpdateConstraints) {
if (updateConstraintsCalled == NO) {
updateConstraintsCalled = YES;
[self updateWrappingConstrains];
updateConstraintsCalled = NO;
}
[super updateConstraints];
}
}
-(NSMutableArray *)wrapConstrains {
if (_wrapConstrains == nil) {
_wrapConstrains = [NSMutableArray new];
}
return _wrapConstrains;
}
-(CGSize)intrinsicContentSize {
return CGSizeMake(UIViewNoIntrinsicMetric, intrinsicHeight);
}
-(void)setViews:(NSArray*)views {
if (self.wrapConstrains.count > 0) {
[UIView autoRemoveConstraints:self.wrapConstrains];
[self.wrapConstrains removeAllObjects];
}
NSArray *subviews = self.subviews;
for (UIView *view in subviews) {
[view removeFromSuperview];
}
for (UIView *view in views) {
view.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:view];
CGFloat leftPadding = 0;
[view autoSetDimension:ALDimensionWidth toSize:CGRectGetWidth(self.frame) - leftPadding relation:NSLayoutRelationLessThanOrEqual];
}
}
-(void)updateWrappingConstrains {
NSArray *subviews = self.subviews;
UIView *previewsView = nil;
CGFloat leftOffset = 0;
CGFloat itemMargin = 5;
CGFloat topPadding = 0;
CGFloat itemVerticalMargin = 5;
CGFloat currentX = leftOffset;
intrinsicHeight = topPadding;
int lineIndex = 0;
for (UIView *view in subviews) {
CGSize size = view.intrinsicContentSize;
if (previewsView) {
[self.wrapConstrains addObject:[view autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:topPadding relation:NSLayoutRelationGreaterThanOrEqual]];
[self.wrapConstrains addObject:[view autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:leftOffset relation:NSLayoutRelationGreaterThanOrEqual]];
CGFloat width = size.width;
currentX += itemMargin;
if (currentX + width <= CGRectGetWidth(self.frame)) {
[self.wrapConstrains addObject:[view autoConstrainAttribute:ALEdgeLeading toAttribute:ALEdgeTrailing ofView:previewsView withOffset:itemMargin relation:NSLayoutRelationEqual]];
[self.wrapConstrains addObject:[view autoAlignAxis:ALAxisBaseline toSameAxisOfView:previewsView]];
currentX += size.width;
}else {
[self.wrapConstrains addObject: [view autoConstrainAttribute:ALEdgeTop toAttribute:ALEdgeBottom ofView:previewsView withOffset:itemVerticalMargin relation:NSLayoutRelationGreaterThanOrEqual]];
currentX = leftOffset + size.width;
intrinsicHeight += size.height + itemVerticalMargin;
lineIndex++;
}
}else {
[self.wrapConstrains addObject:[view autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:topPadding relation:NSLayoutRelationEqual]];
[self.wrapConstrains addObject:[view autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:leftOffset relation:NSLayoutRelationEqual]];
intrinsicHeight += size.height;
currentX += size.width;
}
[view setNeedsUpdateConstraints];
[view updateConstraintsIfNeeded];
[view setNeedsLayout];
[view layoutIfNeeded];
previewsView = view;
}
[self invalidateIntrinsicContentSize];
}
@end
Here I'm using PureLayoutfor defining constrains.
这里我使用PureLayout来定义约束。
You can use this class like this:
你可以像这样使用这个类:
SCHorizontalWrapView *wrappingView = [[SCHorizontalWrapView alloc] initForAutoLayout];
//parentView is some view
[parentView addSubview:wrappingView];
[tagsView autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:padding];
[tagsView autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:padding];
[tagsView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:locationView withOffset:padding relation:NSLayoutRelationGreaterThanOrEqual];
[tagsView setNeedsLayout];
[tagsView layoutIfNeeded];
[tagsView setNeedsUpdateConstraints];
[tagsView updateConstraintsIfNeeded];
NSMutableArray *views = [NSMutableArray new];
//texts is some array of nsstrings
for (NSString *text in texts) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.translatesAutoresizingMaskIntoConstraints = NO;
[button setTitle:text forState:UIControlStateNormal];
button.backgroundColor = [UIColor lightGrayColor];
[views addObject:button];
}
[tagsView setViews:views];