2014-08-27 103 views
1

我希望子视图在超级视图的后沿(具有固定宽度)和自动'换行'当超级视图的后沿到达了。 我正在寻找一种解决方案,使Auto Layout能够为我完成整个工作 - without(重新)计算子视图的大小动态变化时的行结构(例如,以其内容为增长/缩小的文本字段)和添加或删除约束AutoLayout(> iOS 6)子视图的自动和动态换行

我已经有一个(工作)解决方案,根据子视图的预计算(重新)计算的行结构创建约束。

所以我的问题是关于是否可以找到一个解决方案,在这样的星座中使用约束(灵活性,优先级aso),使Auto Layout能够在需要时自动在子视图上执行换行(在第一次加载和运行时)。

我创建了一个示例项目UILabels作为'子视图'。请玩弄这个 - 或试图说服我,我的想法是有远见的 - 但目前还没有实现。

实际屏幕截图代码如下:

labels drawn above each other according to priorities set in addConstraintsForLabels:superview: method

这是测试动态新行自动布局的方法,包括:延伸NSLayoutConstraint

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

导入的类别:

@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; 
} 
+0

请将您提供的代码缩减为包含您问题的部分。您应该自己找出调用错误的原因,而不是委托给社区! – 2014-08-27 18:30:14

+0

我既没有错误问题,也不知道上面的截图结果来自哪里。不过,为了更清晰起见,我会稍微减少一些代码。谢谢。 – anneblue 2014-08-28 08:14:29

回答

0

这对于Autolayout来说是一项相当艰巨的任务。随着您添加越来越多的约束条件,约束条件的求解时间将呈指数级增长。它看起来像UICollectionView可能更适合您的需求。它已经非常适合做这样的事情,尤其是UICollectionViewFlowLayout布局。

+0

感谢您的回答。所以我关于'UICollectionViewFlowLayout'的问题是:'UICollectionView'能够处理包含不断增长的'UITextField'的'UICollectionViewCell'吗?我的意思是,'UICollectionView'是否适用于这种动态单元格,或者每当用户更改textField的一个字符时,是否必须重新加载'UICollectionView'? – anneblue 2014-08-29 08:11:35

+0

好问题! CollectionView能够为初学者重新加载一个单元格。布局也可以使布局属性仅针对单个单元格失效(iOS 8使这更简单,但我认为它也可能在iOS 7中)。这可能是你最好的办法,虽然我没有太多的经验做这件事。 – Acey 2014-08-29 15:55:49

+0

您可能需要使用的另一个“技巧”是将TextView从单元格中移出,因为它成为第一个响应者。当用户键入时,确定单元格的宽度,使布局无效(或重新加载)单元格,但也要更新textField的框架以“跟随”单元格。这样,如果单元格确实需要重新加载,您不会冒险在该文本视图中丢失第一响应者。使布局属性无效这可能不是必要的,但firstResponder可以是一个善变的野兽。 – Acey 2014-08-29 15:59:43