如何使用AutoLayout将UIButtons定位在水平线(包裹,左对齐)?

我需要在我的应用程序(iOS 6.0及更高版本)中以编程方式创建几个具有各种宽度的UIButton。

我希望以“环绕”样式显示按钮:从左边缘开始,每个按钮应水平放置(按照定义的顺序),如果按钮不适合当前的“线” ,它应该在前一行下面的左边缘开始一个新行。

注意:我不想要一个表/网格,因为按钮的宽度不同,我希望它们彼此相邻。

布局示例

我可以在我的代码中手动计算每个按钮的框架,但是我应该使用AutoLayout(使用编程创建的NSLayoutConstraints)吗? 我究竟需要如何设置它?

编辑:在阅读“iOS 6 by Tutorials”的第4章“中间自动布局”后,我不确定使用纯AutoLayout是否可以实现我需要的这种“环绕”function。

我当前的解决方案如下所示:没有AutoLayout,但为每种情况手动设置正确的约束(第一个按钮,新行中最左边的按钮,任何其他按钮)。

(我的猜测是,直接为每个按钮设置框架会导致代码比使用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 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]; } 

您可以使用集合视图,而不是使用Autolayout,您可以使用更好的选项来布置按钮等元素。

它也能够更好地处理旋转下的布局。

这是我们如何使用自动布局实现包装布局的另一个示例:

  @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 

这里我使用PureLayout来定义约束。

您可以像这样使用此类:

 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];