2014-08-27 107 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