Evenly space multiple views within a container view

后端 未结 29 2480
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-22 06:14

Auto Layout is making my life difficult. In theory, it was going to be really useful when I switched, but I seem to fight it all of the time.

I\'ve made a demo proje

相关标签:
29条回答
  • 2020-11-22 06:45

    Yes, you can do this solely in interface builder and without writing code - the one caveat is that you are resizing the label instead of distributing whitespace. In this case, align Label 2's X and Y to the superview so it is fixed in the center. Then set label 1's vertical space to the superview and to label 2 to the standard, repeat for label 3. After setting label 2 the easiest way to set label 1 and 3 is to resize them until they snap.

    Here is the horizontal display, note that the vertical space between label 1 and 2 is set to standard:horizontal display

    And here is the portrait version:enter image description here

    I realize they are not absolutely 100% equally spaced between the baselines due to the difference between the standard space between labels and the standard space to the superview. If that bothers you, set the size to 0 instead of standard

    0 讨论(0)
  • 2020-11-22 06:46

    I found a perfect and simple method. The auto layout does not allow you to resize the spaces equally, but it does allow you to resize views equally. Simply put some invisible views in between your fields and tell auto layout to keep them the same size. It works perfectly!

    Initial XIB

    Stretched XIB

    One thing of note though; when I reduced the size in the interface designer, sometimes it got confused and left a label where it was, and it had a conflict if the size was changed by an odd amount. Otherwise it worked perfectly.

    edit: I found that the conflict became a problem. Because of that, I took one of the spacing constraints, deleted it and replaced it with two constraints, a greater-than-or-equal and a less-than-or-equal. Both were the same size and had a much lower priority than the other constraints. The result was no further conflict.

    0 讨论(0)
  • 2020-11-22 06:47

    Many answers are not correct, but get many counts. Here I just write a solution programmatically, the three views are horizontal align, without using spacer views, but it only work when the widths of labels are known when used in storyboard.

    NSDictionary *views = NSDictionaryOfVariableBindings(_redView, _yellowView, _blueView);
    
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|->=0-[_redView(40)]->=0-[_yellowView(40)]->=0-[_blueView(40)]->=0-|" options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:nil views:views]];
    
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_redView(60)]" options:0 metrics:nil views:views]];
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_redView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
    
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_redView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeLeading multiplier:0.5 constant:0]];
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_blueView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeLeading multiplier:1.5 constant:40]];
    
    0 讨论(0)
  • 2020-11-22 06:48

    I have made a function that might help. This usage example :

     [self.view addConstraints: [NSLayoutConstraint fluidConstraintWithItems:NSDictionaryOfVariableBindings(button1, button2, button3)
                                                                    asString:@[@"button1", @"button2", @"button3"]
                                                                   alignAxis:@"V"
                                                              verticalMargin:100
                                                            horizontalMargin:50
                                                                 innerMargin:25]];
    

    will cause that vertical distribution (sorry don't have the 10 reputation to embed images). And if you change the axis and some margin values :

    alignAxis:@"H"
    verticalMargin:120
    horizontalMargin:20
    innerMargin:10
    

    You'll get that horizontal distribution.

    I'm newbie in iOS but voilà !

    EvenDistribution.h

    @interface NSLayoutConstraint (EvenDistribution)
    
    /**
     * Returns constraints that will cause a set of subviews
     * to be evenly distributed along an axis.
     */
    + (NSArray *)  fluidConstraintWithItems:(NSDictionary *) views
                                   asString:(NSArray *) stringViews
                                  alignAxis:(NSString *) axis
                             verticalMargin:(NSUInteger) vMargin
                           horizontalMargin:(NSUInteger) hMargin
                                innerMargin:(NSUInteger) inner;
    @end
    

    EvenDistribution.m

    #import "EvenDistribution.h"
    
    @implementation NSLayoutConstraint (EvenDistribution)
    
    + (NSArray *) fluidConstraintWithItems:(NSDictionary *) dictViews
                                  asString:(NSArray *) stringViews
                                 alignAxis:(NSString *) axis
                            verticalMargin:(NSUInteger) vMargin
                          horizontalMargin:(NSUInteger) hMargin
                               innerMargin:(NSUInteger) iMargin
    
    {
        NSMutableArray *constraints = [NSMutableArray arrayWithCapacity: dictViews.count];
        NSMutableString *globalFormat = [NSMutableString stringWithFormat:@"%@:|-%d-",
                                         axis,
                                         [axis isEqualToString:@"V"] ? vMargin : hMargin
                                         ];
    
    
    
            for (NSUInteger i = 0; i < dictViews.count; i++) {
    
                if (i == 0)
                    [globalFormat appendString:[NSString stringWithFormat: @"[%@]-%d-", stringViews[i], iMargin]];
                else if(i == dictViews.count - 1)
                    [globalFormat appendString:[NSString stringWithFormat: @"[%@(==%@)]-", stringViews[i], stringViews[i-1]]];
                else
                   [globalFormat appendString:[NSString stringWithFormat: @"[%@(==%@)]-%d-", stringViews[i], stringViews[i-1], iMargin]];
    
                NSString *localFormat = [NSString stringWithFormat: @"%@:|-%d-[%@]-%d-|",
                                         [axis isEqualToString:@"V"] ? @"H" : @"V",
                                         [axis isEqualToString:@"V"] ? hMargin : vMargin,
                                         stringViews[i],
                                         [axis isEqualToString:@"V"] ? hMargin : vMargin];
    
                [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:localFormat
                                                                                         options:0
                                                                                         metrics:nil
                                                                                           views:dictViews]];
    
    
        }
        [globalFormat appendString:[NSString stringWithFormat:@"%d-|",
                                    [axis isEqualToString:@"V"] ? vMargin : hMargin
                                    ]];
    
        [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:globalFormat
                                                                                 options:0
                                                                                 metrics:nil
                                                                                   views:dictViews]];
    
        return constraints;
    
    }
    
    @end
    
    0 讨论(0)
  • 2020-11-22 06:50

    I know it's been a while since the first answer, but I just came across the very same problem and I want to share my solution. For generations to come...

    I set my views on viewDidLoad:

    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        cancelButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
        cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
        [cancelButton setTitle:@"Cancel" forState:UIControlStateNormal];
        [self.view addSubview:cancelButton];
    
        middleButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
        middleButton.translatesAutoresizingMaskIntoConstraints = NO;
        [middleButton setTitle:@"Middle" forState:UIControlStateNormal];
        [self.view addSubview:middleButton];
    
        nextButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
        nextButton.translatesAutoresizingMaskIntoConstraints = NO;
        [nextButton setTitle:@"Next" forState:UIControlStateNormal];
        [self.view addSubview:nextButton];
    
    
        [self.view setNeedsUpdateConstraints];
    
    }
    

    And then, on updateViewConstrains, first I delete all constrains, then I create the views dictionary and then I calculate the space to be used between views. After that, I just use the Visual Language Format to set the constraints:

    - (void)updateViewConstraints {
    
    
        [super updateViewConstraints];
    
        [self.view removeConstraints:self.view.constraints];
    
        NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(cancelButton, nextButton, middleButton);
    
        float distance=(self.view.bounds.size.width-cancelButton.intrinsicContentSize.width-nextButton.intrinsicContentSize.width-middleButton.intrinsicContentSize.width-20-20)/  ([viewsDictionary count]-1);  // 2 times 20 counts for the left & rigth margins
        NSNumber *distancies=[NSNumber numberWithFloat:distance];
    
    //    NSLog(@"Distancies: %@", distancies);
    //    
    //    NSLog(@"View Width: %f", self.view.bounds.size.width);
    //    NSLog(@"Cancel Width: %f", cancelButton.intrinsicContentSize.width);
    //    NSLog(@"Middle Width: %f", middleButton.intrinsicContentSize.width);
    //    NSLog(@"Next Width: %f", nextButton.intrinsicContentSize.width);
    
    
    
        NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-[cancelButton]-dis-[middleButton]-dis-[nextButton]-|"
                                                                       options:NSLayoutFormatAlignAllBaseline
                                                                       metrics:@{@"dis":distancies}
                                                                         views:viewsDictionary];
    
    
        [self.view addConstraints:constraints];
    
    
    
        constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[nextButton]-|"
                                                              options:0
                                                              metrics:nil
                                                                views:viewsDictionary];
        [self.view addConstraints:constraints];
    
    
    
    }
    

    The good thing about this method is that you have to do very little math. I'm not saying this is the perfect solution, but I works for the layout I was trying to achieve.

    I hope it helps.

    0 讨论(0)
提交回复
热议问题