使用AutoLayout(>;iOS 6)对子视图进行自动和动态换行

使用AutoLayout(>;iOS 6)对子视图进行自动和动态换行,ios,autolayout,nslayoutconstraint,Ios,Autolayout,Nslayoutconstraint,我希望子视图在其superview内水平对齐(具有固定宽度),并在到达superview的后缘时自动“换行”。 我正在寻找一种解决方案,使Auto Layout能够在子视图的大小动态变化时(例如,文本字段随内容的增长/收缩)和添加或删除约束时,为我完成全部工作,而不必重新计算行结构 我已经有了一个(有效的)解决方案,即根据子视图的预先(和重新)计算的行结构(重新)创建约束 所以我的问题是:能否找到一种解决方案,在这样一个星座中使用约束(具有灵活性、优先级a.s.o.),使自动布局能够在需要时(在

我希望子视图在其superview内水平对齐(具有固定宽度),并在到达superview的后缘时自动“换行”。 我正在寻找一种解决方案,使Auto Layout能够在子视图的大小动态变化时(例如,文本字段随内容的增长/收缩)和添加或删除约束时,为我完成全部工作,而不必重新计算行结构

我已经有了一个(有效的)解决方案,即根据子视图的预先(和重新)计算的行结构(重新)创建约束

所以我的问题是:能否找到一种解决方案,在这样一个星座中使用约束(具有灵活性、优先级a.s.o.),使自动布局能够在需要时(在首次加载和运行时)在子视图上自动断线。

我创建了一个示例项目,其中
UILabels
作为“子视图”。请你玩玩这个,或者试着说服我,我的想法只是有远见的,但目前还无法实现

以下代码的实际屏幕截图:

这是测试动态新线自动布局的方法:

- (void) addConstraintsForLabels : (NSArray*) labels
                   superview : (UIView*) superview {

    for (int i = 0; i < labels.count; i++) {
        UILabel* precedingLabel = i == 0 ? nil : labels[i - 1];
        UILabel* label = labels[i];
        [label setTranslatesAutoresizingMaskIntoConstraints: NO];

        //// set all contentHuggingPriorities to no growing and no compression
        [label setContentHuggingPriority: UILayoutPriorityRequired forAxis: UILayoutConstraintAxisHorizontal];
        [label setContentHuggingPriority: UILayoutPriorityRequired forAxis: UILayoutConstraintAxisVertical];
        [label setContentCompressionResistancePriority: UILayoutPriorityRequired forAxis: UILayoutConstraintAxisHorizontal];
        [label setContentCompressionResistancePriority: UILayoutPriorityRequired forAxis: UILayoutConstraintAxisVertical];

        //// constraints affecting superview
        // top to superview
        NSLayoutConstraint* topToSup =  [NSLayoutConstraint alignEdge: NSLayoutAttributeTop                                                      ofViews: @[label, superview]                                                           relation: i == 0 ? NSLayoutRelationEqual : NSLayoutRelationGreaterThanOrEqual
                                                          spacing: 0];
        // leading to superview
        NSLayoutConstraint* leadingToSup = [NSLayoutConstraint alignEdge: NSLayoutAttributeLeading
                                                             ofViews: @[label, superview]
                                                            relation: i == 0 ? NSLayoutRelationEqual : NSLayoutRelationGreaterThanOrEqual
                                                             spacing: 0];
        // bottom to superview
        NSLayoutConstraint* bottomToSup = [NSLayoutConstraint alignEdge: NSLayoutAttributeBottom
                                                            ofViews: @[label, superview]
                                                           relation: i == labels.count - 1 ? NSLayoutRelationEqual : NSLayoutRelationLessThanOrEqual
                                                            spacing: 0];
        // trailing to superview
        NSLayoutConstraint* trailingToSup = [NSLayoutConstraint alignEdge: NSLayoutAttributeTrailing
                                                              ofViews: @[label, superview]
                                                             relation: NSLayoutRelationLessThanOrEqual
                                                              spacing: 0];

        //// example constraints affecting preceding label
        // leading to preceding label
        NSLayoutConstraint* leadingToPrec = precedingLabel == nil ? nil : [NSLayoutConstraint horizontalSpacing: 0
                                                                                               betweenViews: @[precedingLabel, label]
                                                                                                   flexible: NO
                                                                                                  inMaximum: NO];
        // y position to preceding label
        NSLayoutConstraint* centerYToPrec = precedingLabel == nil ? nil :  [NSLayoutConstraint alignEdge: NSLayoutAttributeCenterY
                                                                                             ofViews: @[label, precedingLabel]
                                                                                            relation: NSLayoutRelationEqual
                                                                                             spacing: 0];
        // top to preceding view
        NSLayoutConstraint* topToPrec = precedingLabel ==  nil ? nil : [NSLayoutConstraint verticalSpacing: 0
                                                                                               ofViews: @[precedingLabel, label]
                                                                                              flexible: NO
                                                                                             inMaximum: NO];

        //// priorities
        // affecting the superview
        topToSup.priority = UILayoutPriorityRequired; // is either flexible or non-flexible for the first
        leadingToSup.priority = UILayoutPriorityRequired; // is either flexible or non-flexible for the first
        bottomToSup.priority = UILayoutPriorityRequired; // is either flexible or non-flexible for the last
        trailingToSup.priority = UILayoutPriorityRequired; // it is required, that the view does not exceed the right edge of its superview
        [superview addConstraints: @[trailingToSup, topToSup, leadingToSup, bottomToSup]];

        // affecting the preceding view
        if (i > 0) {
            leadingToPrec.priority = UILayoutPriorityDefaultHigh;
            centerYToPrec.priority = UILayoutPriorityDefaultHigh;
            topToPrec.priority = UILayoutPriorityDefaultHigh;
            [superview addConstraints: @[leadingToPrec, centerYToPrec, topToPrec]];
        }
    }
}

对于自动布局来说,这是一项非常艰巨的任务。随着添加越来越多的约束,约束的求解时间呈指数增长。看起来UICollectionView可能更适合您的需要。它已经非常适合这样做,特别是UICollectionViewFlowLayout布局。

请将提供的代码缩减到包含问题的部分。你应该自己找出是什么引起了这个错误,而不是把它委托给社区!我既没有错误问题,也不知道上面的截图结果来自哪里。但无论如何,为了更清晰,我将稍微减少代码。谢谢,谢谢你的回答。因此,我关于
UICollectionViewFlowLayout
的问题是:
UICollectionView
是否能够处理一个
UICollectionViewCell
,其中包含一个不断增长的
UITextField
?我的意思是,
UICollectionView
是否可以处理这样的动态单元格,还是每次用户因为文本字段的大小变化而更改一个字符时,我都必须重新加载
UICollectionView
?问得好!CollectionView能够为启动器重新加载一个单元。布局还能够使单个单元格的布局属性无效(iOS 8使这更容易,但我认为在iOS 7中也可以)。这可能是你最好的方法,尽管我在这方面没有太多的经验。你可能需要使用的另一个“技巧”是在TextView成为第一响应者时将其从手机中取出。当用户键入时,确定单元格的宽度,使单元格的布局无效(或重新加载),同时更新文本字段的框架以“跟随”单元格。这样,如果单元格确实需要重新加载,您就不会冒失去该文本视图上的第一响应程序的风险。通过使布局属性无效,这可能不是必需的,但第一响应者可能是一个变化无常的野兽。
@implementation NSLayoutConstraint (ConstructorAdditions)


// align edges of two views
// if one view is the superview, put it at the second position in the array
+ (NSLayoutConstraint*) alignEdge : (NSLayoutAttribute) edge
                      ofViews : (NSArray*) /*UIView*/ views
                     relation : (NSLayoutRelation) relation
                      spacing : (CGFloat) spacing {
    NSLayoutConstraint* constraint = nil;
    if (views.count == 2) {
        if (edge == NSLayoutAttributeBaseline || edge == NSLayoutAttributeTrailing || edge == NSLayoutAttributeBottom || edge == NSLayoutAttributeRight) {
            spacing = -spacing;
        }
        constraint = [NSLayoutConstraint constraintWithItem : [views objectAtIndex: 0]
                                              attribute : edge
                                              relatedBy : relation
                                                 toItem : [views objectAtIndex: 1]
                                              attribute : edge
                                             multiplier : 1.0
                                               constant : spacing];
    }
    return constraint;
}


// vertical spcacing between views
// the method assumes, that the first view in the array is above the second view
+ (NSLayoutConstraint*) verticalSpacing : (CGFloat) spacing
                            ofViews : (NSArray*) /*UIView*/ views
                           flexible : (BOOL) flexible
                           inMaximum: (BOOL) inMax {
    NSLayoutConstraint* constraint = nil;

    NSLayoutRelation relation = flexible ? (inMax ? NSLayoutRelationLessThanOrEqual : NSLayoutRelationGreaterThanOrEqual) : NSLayoutRelationEqual;

    if (views.count == 2) {
        constraint = [NSLayoutConstraint constraintWithItem : [views objectAtIndex: 0]
                                              attribute : NSLayoutAttributeBottom
                                              relatedBy : relation
                                                 toItem : [views objectAtIndex: 1]
                                              attribute : NSLayoutAttributeTop
                                             multiplier : 1.0
                                               constant : -spacing];
    }
    return constraint;
}


// horizontal spacing between views
// the method assumes, that the first view in the array is left of the second view
+ (NSLayoutConstraint*) horizontalSpacing : (CGFloat) spacing
                         betweenViews : (NSArray*) /*UIView*/ views
                             flexible : (BOOL) flexible
                             inMaximum: (BOOL) inMax {
    NSLayoutConstraint* constraint = nil;
    NSLayoutRelation relation = flexible ? (inMax ? NSLayoutRelationLessThanOrEqual : NSLayoutRelationGreaterThanOrEqual) : NSLayoutRelationEqual;
    if (views.count == 2) {
        constraint = [NSLayoutConstraint constraintWithItem : [views objectAtIndex: 0]
                                              attribute : NSLayoutAttributeTrailing
                                              relatedBy : relation
                                                 toItem : [views objectAtIndex: 1]
                                              attribute : NSLayoutAttributeLeading
                                             multiplier : 1.0
                                               constant : -spacing];
    }
    return constraint;
}



+ (NSArray*) equalWidthOfViews : (NSArray*) views
                    toView : (UIView*) view
                  distance : (CGFloat) distance
                  relation : (NSLayoutRelation) relation
                multiplier : (CGFloat) multiplier {
    NSMutableArray* constraints = [[NSMutableArray alloc] initWithCapacity: views.count];

    for (UIView* aView in views) {
        [constraints addObject: [NSLayoutConstraint  constraintWithItem : aView
                                                          attribute : NSLayoutAttributeWidth
                                                          relatedBy : relation
                                                             toItem : view
                                                          attribute : NSLayoutAttributeWidth
                                                         multiplier : multiplier
                                                           constant : distance]];
    }
    return constraints;
}