Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use AutoLayout to position UIButtons in horizontal lines (wrapping, left aligned)?

I need to create a couple of UIButtons with various widths programmatically in my app (iOS 6.0 and above).

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.

layout example

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?

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.

like image 670
thomers Avatar asked Nov 06 '13 09:11

thomers


People also ask

What is the use of auto layout?

Auto Layout defines your user interface using a series of constraints. Constraints typically represent a relationship between two views. Auto Layout then calculates the size and location of each view based on these constraints. This produces layouts that dynamically respond to both internal and external changes.

What are auto layout constraints?

Constraints allow you to control how objects respond to changing frames, while auto layout allows you to control how frames respond to changing objects. In this article, I'll explain how they work and when to use each.


2 Answers

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).

(My guess is that setting the frame for each button directly would result in more readable code than using NSLayoutConstraints, anyway)

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];
}
like image 185
thomers Avatar answered Oct 09 '22 12:10

thomers


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.

like image 37
Abizern Avatar answered Oct 09 '22 13:10

Abizern