【问题标题】:Layout constraints for dynamically created buttons动态创建按钮的布局约束
【发布时间】:2015-07-20 16:21:33
【问题描述】:

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

创建和添加按钮以查看

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

     }

   }
}

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

不可能在没有准备好约束的视图层次结构中设置布局

【问题讨论】:

  • 你一次要求太多了。请把它分解为一个问题并询问。没有人愿意为你修复 50 行代码。
  • 感谢您的帮助,我已删除不属于问题的块。我希望它可以帮助其他人现在理解这个问题。

标签: ios objective-c autolayout nslayoutconstraint


【解决方案1】:

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

    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),除了第一遍之外,当然您需要这样做每一次通过。

一旦我们有了可以工作的代码,我们就可以开始修改它以接近您想要做的事情。例如,现在可以轻松更改硬编码间距以使用类似您的变量:

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;

等等。重点是:这就是我“发展我的代码”的方式,总是从简单和不断发展的开始,确保它在每次迭代中都能正常工作,直到我真正想做的事情。去吧,照样做!

【讨论】:

  • 我已经消除了混乱。您提到的更改仍然会引发相同的错误
  • 请问您认为这应该做什么:"H:|-%f-[btnView]|" %f 到底是什么? - 哦,我明白了。看,这可能与它无关,但这不是您将数字插入视觉格式的方式。使用度量字典;这就是它的用途。
  • 对不起,我还是一头雾水。 prevXprevObject 是什么?我们如何知道事情正在按照您期望的顺序发生?
  • 也许这有助于进一步解决问题。这是一个包含四个按钮标题的数组:@[@"Yellow", @"Purple", @"Blue", @"Red"]。你能写出水平排列四个带有这些标题的按钮的代码吗?
  • prevX 是我用来检查是否应该水平对齐到超级视图的计算值,我会在创建第一个按钮后更新它,以便我可以在 2 个按钮之间应用约束。 prevObject 是相对于新按钮应该匹配约束的按钮。通过创建静态按钮,我可以放置约束,但如果它处于循环中并且必须动态计算约束,则不能
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多