动态创建按钮的布局约束

Layout constraints for dynamically created buttons

我正在尝试使用动态创建的按钮创建视图。我发现很难为第一个创建的内部对象设置约束。问题出在哪里?

创建并添加要查看的按钮

-(void) createButton:(NSString *) btnText isButton:(BOOL) type  phraseWidth:(NSInteger) width view:(UIView *) currentView {
if (!type) {  // if it's a button then create label & button at same place else only create button
             // align left to prev button, align baseline
    if (prevX == 5) { // button left aligned to rowView, right align none
        UIButton *btnView = [[UIButton alloc] init];
        btnView.translatesAutoresizingMaskIntoConstraints=NO;
        [currentView addSubview:btnView];
       NSDictionary *dictScrollConst = NSDictionaryOfVariableBindings(btnView);
        NSString *hConstraint = [NSString stringWithFormat:@"H:|-%f-[btnView]|",prevX];
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:hConstraint options:0 metrics:nil views:dictScrollConst]];
        NSString *vConstraint = @"V:|[btnView]|";
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vConstraint options:0 metrics:nil views:dictScrollConst]];
        prevObject = btnView;

    }
    else { // align new button to previous button
        UIButton *btnView = [[UIButton alloc] init];
        btnView.translatesAutoresizingMaskIntoConstraints=NO;
        [currentView addSubview:btnView];
        NSDictionary *dictScrollConst = NSDictionaryOfVariableBindings(prevObject,btnView);
        NSString *hConstraint = [NSString stringWithFormat:@"H:[prevObject]-%d-[btnView]",kHorizontalSidePadding];
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:hConstraint options:0 metrics:nil views:dictScrollConst]];
        NSString *vConstraint = @"V:|[btnView]|";
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vConstraint options:0 metrics:nil views:dictScrollConst]];

     }

   }
}

不允许针对之前创建的按钮添加约束。抛出异常:

Impossible to set up layout with view hierarchy unprepared for constraint

还有太多的代码让我无法弄清楚发生了什么,但这很明显:

    NSString *vConstraint = @"V:|[btnView]|";
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vConstraint options:0 metrics:nil views:dictScrollConst]];
    [currentView addSubview:btnView];

这些行的顺序错误。当视图不在视图层次结构中时,您不能添加涉及视图(此处为 btnView)的约束。 (这正是错误消息告诉您的内容,尽管它使用了相当含糊的术语。)

所以,添加子视图。 然后 添加影响它的约束。


我建议您做的就是我一直做的:从非常简单开始,然后逐步解决实际问题。因此,我建议您从布局的第二行开始作为练习,看看您是否可以做这个简单的练习:给定标题数组 @[@"Yellow", @"Purple", @"Blue", @"Red"],您可以使用它来生成水平的四个按钮吗?

这是 my 的代码。请注意它是多么清晰和简单——非常合乎逻辑、简明扼要、简单明了。我们以后总是可以添加调整,但这是您需要尝试维护和构建的简单性,这样您就不会混淆自己:

NSArray* titles = @[@"Yellow", @"Purple", @"Blue", @"Red"];
UIView* previousButton = nil;
for (NSInteger i = 0; i < 4; i++) {
    UIButton* b = [UIButton buttonWithType:UIButtonTypeSystem];
    [b setTitle:titles[i] forState:UIControlStateNormal];
    [b setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self.view addSubview:b];
    [self.view addConstraints:
     [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(100)-[b]" 
      options:0 metrics:nil views:@{@"b":b}]];
    if (i == 0) {
        [self.view addConstraints:
         [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(50)-[b]" 
          options:0 metrics:nil views:@{@"b":b}]];
    } else {
        [self.view addConstraints:
         [NSLayoutConstraint constraintsWithVisualFormat:@"H:[p]-(20)-[b]" 
          options:0 metrics:nil views:@{@"b":b, @"p":previousButton}]];
    }
    previousButton = b;
}

鉴于此,我们立即发现您的代码有一个问题:没有证据表明您在除了第一遍之外的任何地方都设置了上一个按钮(您的 prevObject),当然,当您需要在 次通过时执行此操作。

一旦我们有了可以运行的代码,我们就可以开始修改它以接近您希望执行的操作。例如,现在很容易更改 hard-coded 间距以使用像您这样的变量:

NSArray* titles = @[@"Yellow", @"Purple", @"Blue", @"Red"];
UIView* previousButton = nil;
NSInteger initialX = 5; // *
NSInteger horizSpace = 10; // *
for (NSInteger i = 0; i < 4; i++) {
    UIButton* b = [UIButton buttonWithType:UIButtonTypeSystem];
    [b setTitle:titles[i] forState:UIControlStateNormal];
    [b setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self.view addSubview:b];
    [self.view addConstraints:
     [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(100)-[b]" 
       options:0 metrics:nil views:@{@"b":b}]];
    if (i == 0) {
        [self.view addConstraints:
         [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(initialX)-[b]" 
          options:0 metrics:@{@"initialX":@(initialX)} views:@{@"b":b}]];
    } else {
        [self.view addConstraints:
         [NSLayoutConstraint constraintsWithVisualFormat:@"H:[p]-(horizSpace)-[b]" 
          options:0 metrics:@{@"horizSpace":@(horizSpace)} views:@{@"b":b, @"p":previousButton}]];
    }
    previousButton = b;

等等。重点是:这就是我 "grow my code" 的方式,总是从简单的开始并不断发展,确保它在每次迭代中都能正常工作,直到我真正想做的事情。去你做同样的事情!