【问题标题】:NSLayoutConstraint for unknown number of subviews in codeNSLayoutConstraint 用于代码中未知数量的子视图
【发布时间】:2016-07-15 14:38:41
【问题描述】:

我正在进入 iOS 编程领域,并且或多或少地掌握了具有固定数量项目的自动布局。比如说,我们有一个 UILabel 代表标题,UILabel 代表副标题,那么约束的视觉格式是'V:|-[title]-10-[subtitle]-|'

但是如果我根据一些 API 响应动态创建子视图会怎样。例如,我需要添加 40 个子视图。我用视觉格式指定每个子视图并跟踪它们已经不现实了。正确的方法是什么?

我想,在我添加每个子视图之后,我会根据前一个视图使用constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant: 设置约束。这是要走的路,还是有更好的方法?

【问题讨论】:

    标签: ios


    【解决方案1】:

    我用视觉格式指定每个子视图并跟踪它们已经不现实了

    为什么不呢?您似乎认为这是某种“非此即彼”的情况。没有什么能阻止您使用可视化格式来构建约束集合。没有法律规定您必须将所有您的约束放入一个视觉格式。

    而且布局引擎不知道也不关心你用什么表示法来形成你的约束。约束就是约束!

    考虑一下我在其中为滚动视图和其中的 30 个标签构建约束的代码:

        var con = [NSLayoutConstraint]()
        con.appendContentsOf(
            NSLayoutConstraint.constraintsWithVisualFormat(
                "H:|[sv]|",
                options:[], metrics:nil,
                views:["sv":sv]))
        con.appendContentsOf(
            NSLayoutConstraint.constraintsWithVisualFormat(
                "V:|[sv]|",
                options:[], metrics:nil,
                views:["sv":sv]))
        var previousLab : UILabel? = nil
        for i in 0 ..< 30 {
            let lab = UILabel()
            // lab.backgroundColor = UIColor.redColor()
            lab.translatesAutoresizingMaskIntoConstraints = false
            lab.text = "This is label \(i+1)"
            sv.addSubview(lab)
            con.appendContentsOf(
                NSLayoutConstraint.constraintsWithVisualFormat(
                    "H:|-(10)-[lab]",
                    options:[], metrics:nil,
                    views:["lab":lab]))
            if previousLab == nil { // first one, pin to top
                con.appendContentsOf(
                    NSLayoutConstraint.constraintsWithVisualFormat(
                        "V:|-(10)-[lab]",
                        options:[], metrics:nil,
                        views:["lab":lab]))
            } else { // all others, pin to previous
                con.appendContentsOf(
                    NSLayoutConstraint.constraintsWithVisualFormat(
                        "V:[prev]-(10)-[lab]",
                        options:[], metrics:nil,
                        views:["lab":lab, "prev":previousLab!]))
            }
            previousLab = lab
        }
        con.appendContentsOf(
            NSLayoutConstraint.constraintsWithVisualFormat(
                "V:[lab]-(10)-|",
                options:[], metrics:nil,
                views:["lab":previousLab!]))
        NSLayoutConstraint.activateConstraints(con)
    

    我正在使用可视化格式,但我在添加视图时一次执行一个约束(这正是您要问的)。

    【讨论】:

    • 哈。现在我觉得有点愚蠢:) 谢谢
    • 请注意,如果您希望滚动视图将其contentSize.height 设置为实际允许滚动,您还需要将最后一个标签限制在滚动视图的底部。
    • @robmayoff 做到了,只是在粘贴时被切断了
    【解决方案2】:

    答案真的取决于你想要什么样的布局,因为有更高级的工具可以制作一些布局。

    从 iOS 9 开始,您可以使用UIStackView 来管理一行或一列视图。 By nesting stack views, you can easily make a grid of views. 要了解UIStackView,请先观看“Mysteries of Auto Layout, Part 1” from WWDC 2015。如果您在代码中完成所有布局,则可以使用TZStackView,这是UIStackView 的重新实现,适用于 iOS 7 及更高版本。

    UICollectionView 是另一种用于在网格或许多其他排列中布置视图的工具。它比UIStackView 更复杂,但是有很多介绍性材料可以教你如何使用它,例如“Introducing Collection Views” from WWDC 2012

    【讨论】:

      【解决方案3】:

      我构建了一个动态布局引擎(水平布局和垂直布局)。

      1. 它接受一组视图。

      2. 它将视图作为子视图添加到容器中。

      3. 然后动态组成一个垂直和水平的视觉布局字符串。 (它为使用 v%p 布局的每个视图生成一个唯一的“对象地址”字符串。
      4. 只需在 viewDidLoad 中调用:[myView addControls:@[view1, view2, view3]],它就会为您垂直布局。
      5. 这是一个用于垂直布局的通用布局引擎。享受吧。

        @implementation UIView (VerticalLayout)
        
        - (void)addControls:(NSArray*)controls
            align:(VerticalAlignment)alignment
            withHeight:(CGFloat)height
            verticalPadding:(CGFloat)verticalPadding
            horizontalPadding:(CGFloat)horizontalPadding
            topPadding:(CGFloat)topPadding
            bottomPadding:(CGFloat)bottomPadding
            withWidth:(CGFloat)width
        {
            NSMutableDictionary* bindings = [[NSMutableDictionary alloc] init];
            NSDictionary *metrics = @{
                @"topPadding": @(topPadding),
                @"bottomPadding": @(bottomPadding),
                @"verticalPadding": @(verticalPadding),
                @"horizontalPadding":@(horizontalPadding) };
        
            NSMutableString* verticalConstraint = [[NSMutableString alloc] initWithString:@"V:"];
            if (alignment == VerticalAlignTop || alignment == VerticalAlignStretchToFullHeight) {
                [verticalConstraint appendString:@"|"];
            }
        
            for (UIView* view in controls) {
                BOOL isFirstView = [controls firstObject] == view;
                BOOL isLastView = [controls lastObject] == view;
        
                [self addSubview: view];
        
                // Add to vertical constraint string
                NSString* viewName = [NSString stringWithFormat:@"v%p",view];
                [bindings setObject:view forKey:viewName];
        
                NSString* yLeadingPaddingString = @"";
                NSString* yTrailingPadding = @"";
                if (isFirstView && topPadding > 0) {
                    yLeadingPaddingString = @"-topPadding-";
                }
                else if (!isFirstView && verticalPadding > 0) {
                    yLeadingPaddingString = @"-verticalPadding-";
                }
        
                if (isLastView && bottomPadding > 0) {
                    yTrailingPadding = @"-bottomPadding-";
                }
        
                [verticalConstraint appendFormat:@"%@[%@%@]%@",
                    yLeadingPaddingString,
                    viewName,
                    height > 0 ? [NSString stringWithFormat:@"(%f)", height] : @"",
                    yTrailingPadding];
        
                NSArray* constraints = [NSLayoutConstraint constraintsWithVisualFormat:
                        [NSString stringWithFormat: @"H:|-horizontalPadding-[%@%@]%@|",
                        viewName,
                        (width > 0 ? [NSString stringWithFormat:@"(%lf)", width] : @""),
                        width > 0 ? @"-" : @"-horizontalPadding-"]
                    options:NSLayoutFormatDirectionLeadingToTrailing
                    metrics:metrics
                    views:bindings];
        
                // high priority, but not required.
                for (NSLayoutConstraint* constraint in constraints) {
                    constraint.priority = 900;
                }
        
                // Add the horizontal constraint
                [self addConstraints: constraints];
            }
        
            if (alignment == VerticalAlignBottom || alignment == VerticalAlignStretchToFullHeight) {
                [verticalConstraint appendString:@"|"];
            }
        
            NSArray* constraints = [NSLayoutConstraint
                    constraintsWithVisualFormat:verticalConstraint
                    options:NSLayoutFormatAlignAllLeading
                    metrics:metrics
                    views:bindings];
            for (NSLayoutConstraint* constraint in constraints) {
                constraint.priority = 900;
            }
        
            // Add the vertical constraints for all these views.
            [self addConstraints:constraints];
        }
        

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-05-06
        • 2021-07-07
        • 2021-05-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多