swift之Auto Layout学习

1、What's Auto Layout

    Auto Layout是由苹果公司UIKit框架提供的一个用于动态计算UIView及其子类的大小和位置的库。

     说到Auto Layout就不得不说Cassowary算法,因为Auto Layout是构建在Cassowary算法的基础之上的。1997年,Auto Layout用到的布局算法论文发表,被称为高效的线性方程求解算法。2011年苹果利用Cassowary算法为开发者提供了Auto Layout自动布局库中。由于Cassowary算法的本身的优秀,不仅是苹果公司,许多开发者将其运用到各个不同的开发语言中,如JavaScript、ASP.NET、Java、C++等都有运用Cassowary算法的库。从这里也可以看出Cassowary算法自身的优秀和先进性,不然不会被运用的如此广泛。

   苹果公司在iOS 6系统时引入了Auto Layout,但是直到现在已经更新到iOS 12了,还有很多开发者还是不愿使用Auto Layout。主要是对其反人类的语法以及对其性能问题的担忧。

   针对Auto Layout的一些问题,在iOS 9发布时,苹果推出了更简洁语法的NSLayoutAnchor。同时发布了模仿前端Flexbox布局思路的UIStackView,以此为开发者在自动布局上提供更好的选择。

    从iOS 6iOS 12,苹果也在不断的优化Auto Layout的性能,同时为开发者提供更简洁的API,如果你还在使用frame手写布局,不妨试试Auto Layout。下面我将介绍iOS中几种常用的布局方法。

2、Auto Layout各个版本不同用法

  如我要设置一个宽高为120,居中显示的View,效果如下图:

    

  • 1、用frame手写布局

    let X:CGFloat = 5.0
    let Y:CGFloat = 5.0
    let W:CGFloat = 100.0
    let H:CGFloat = 100.0
    self.contentView?.frame = CGRect(x: X, y: Y, width: W, height: H)
    self.contentView?.center = self.view.center
  • 2、iOS 6提供的NSLayoutConstraint语法添加约束

    self.contentView?.translatesAutoresizingMaskIntoConstraints = false
    let centerXConstranit = NSLayoutConstraint(item:contentView!, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1, constant: 0)
    let centerYConstanit = NSLayoutConstraint(item:contentView!, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1, constant: 0)
    let widthConstanit = NSLayoutConstraint(item: contentView!, attribute:.width, relatedBy: .equal, toItem: self.view, attribute: .width, multiplier: 0, constant: 100.0)
    let heightConstanit = NSLayoutConstraint(item:contentView!, attribute: .height, relatedBy: .equal, toItem: self.view, attribute: .height, multiplier: 0, constant: 100.0)
    self.view.addConstraints([centerXConstranit,centerYConstanit,widthConstanit,heightConstanit])
    • NSLayoutConstraint

      NSLayoutConstraint是继承自NSObject的一个类,也就是用来做约束的一个类。约束是对于视图而言的。 视图可以有许多约束,那么我们怎么给视图添加或者移除约束呢?

      现在来说两种方法给视图添加或者移除约束:

      第一种方法:通过UIView的实例方法来添加和移除自身的约束:(具体看下边 UIView 的扩展)

      extension UIView {
          @available(iOS 6.0, *)
           open var constraints: [NSLayoutConstraint] { get }
                  
          @available(iOS 6.0, *)
           open func addConstraint(_ constraint: NSLayoutConstraint)
                  
          @available(iOS 6.0, *)
           open func addConstraints(_ constraints:
                                              [NSLayoutConstraint])
          @available(iOS 6.0, *)
           open func removeConstraint(_ constraint:
                                              NSLayoutConstraint)
                  
          @available(iOS 6.0, *)
           open func removeConstraints(_ constraints:
                                              [NSLayoutConstraint])
      }

      示例:

      private func testTheConstraintOne(){
          let viewItem = UIView()
          // 一定要禁止 autoresize
          viewItem.translatesAutoresizingMaskIntoConstraints = false
          viewItem.backgroundColor = UIColor.brown
          // 必须先添加到 View上 然后再去做约束 因为约束如果要用到父视图 不提前添加 怎么知道谁是父视图
          view.addSubview(viewItem)
          //  添加和父视图view X 中心对齐
          let centerXConstrains = NSLayoutConstraint( item: viewItem,
                                                                      attribute: .centerX,
                                                                     relatedBy: .equal,
                                                                         toItem: view,
                                                                      attribute: .centerX,
                                                                     multiplier: 1, constant: 0)
              
          //  添加和父视图 Y 中心对其的约束
          let centerYConstrains = NSLayoutConstraint( item: viewItem,
                                                                      attribute: .centerY,
                                                                     relatedBy: .equal,
                                                                         toItem: view,
                                                                      attribute: .centerY,
                                                                     multiplier: 1,
                                                                      constant: 0)
              
          //  添加和父视图 宽的关系 为1:2约束
          let heightConstrains = NSLayoutConstraint( item: viewItem,
                                                                    attribute: .width ,
                                                                   relatedBy: .equal,
                                                                       toItem: view,
                                                                    attribute: .width,
                                                                   multiplier: 1 / 2,
                                                                    constant: 0)
          //  添加自身视图宽 高的关系 为1:1 的约束
          let widthConstrains = NSLayoutConstraint( item: viewItem,
                                                                  attribute: .height ,
                                                                 relatedBy: .equal,
                                                                     toItem:  viewItem,
                                                                  attribute: .width,
                                                                 multiplier: 1,
                                                                  constant: 0)
          
          // 一定要分清楚约束是相对于谁加的  加给谁的
          // view.addConstraint(centerXConstrains)
          // view.addConstraint(centerYConstrains)
          // view.addConstraint(heightConstrains)
              
          // 此处可以向上边注释的那样一个一个添加,也可以以一个数组来一起添加
          view.addConstraints([centerXConstrains,
                                              centerYConstrains,
                                                heightConstrains])
          viewItem.addConstraint(widthConstrains)
      }

      第二种方法: 通过NSLayoutConstraint的一个类方法来给视图添加约束,具体如下: 在看NSLayoutConstraint的实例化方法的时候,会发现他还有类方法,我们看下面两个方法:

      // 添加约束
      @available(iOS 8.0, *)
      open class func activate(_ constraints:[NSLayoutConstraint])
      // 移除约束
      @available(iOS 8.0, *)
      open class func deactivate(_ constraints:[NSLayoutConstraint])

      示例

      private func testTheConstraintOne(){
          let view1 = UIView()
          view1.backgroundColor = UIColor.blue
          view1.translatesAutoresizingMaskIntoConstraints = false
          view.addSubview(view1)
          // view1 和 viewItem 的 Y 中心对称
          let topContrians = NSLayoutConstraint(item: view1,
                                           attribute: .top,
                                                 relatedBy: .equal,
                                                    toItem: viewItem,
                                                    attribute: .centerY,
                                                    multiplier: 1,
                                                    constant: 0)
          // view1 和 viewItem 的 bottom 对其
          let bottomContrians = NSLayoutConstraint(item: view1,
                                              attribute: .bottom,
                                              relatedBy: .equal,
                                                 toItem: viewItem,
                                              attribute: .bottom,
                                             multiplier: 1,
                                               constant: 0)
          // view1 和 viewItem 的 width 相等
          let widthContrains = NSLayoutConstraint(item: view1,
                                             attribute: .width,
                                             relatedBy: .equal,
                                                toItem: viewItem,
                                             attribute: .width,
                                            multiplier: 1,
                                              constant: 0)
          // view1 和 viewItem 的 leading 对齐
          let leadingContrains = NSLayoutConstraint(item: view1,
                                               attribute: .leading,
                                               relatedBy: .equal,
                                                  toItem: viewItem,
                                               attribute: .leading,
                                              multiplier: 1,
                                                constant: 0)
          // iOS8 以后 NSLayoutConstraint 的类方法 也可以把约束添加到视图上,而且省掉了判断添加到那个视图上的问题,避免了上面例子中因为视图添加错误而导致的崩溃
          NSLayoutConstraint.activate([topContrians,bottomContrians,widthContrains,leadingContrains])
      }
    • 实例化NSLayoutConstraint

      我们可以看到以上两种方法都需要一个NSLayoutConstraint类型的参数,如何实例化一个NSLayoutConstraint对象呢? 那么我们来看一下 NSLayoutConstraint 的初始化方法:
      public convenience init( item view1: Any,
                               attribute attr1: NSLayoutAttribute,
                         relatedBy relation: NSLayoutRelation,
                                toItem view2: Any?, attribute
                                           attr2: NSLayoutAttribute,
                                     multiplier: CGFloat,
                                   constant c: CGFloat)
      初始化方法中有很多参数,接下来我们来讲一下各个参数的意义:
      item: 可以看到他后边还有一个 view1 ,个人认为一般的他就是一个 UIView 或者其子类对象,是要进行约束的那个视图attribute: 是一个NSLayoutAttribute枚举,可以看到他的枚举值有 left、right、bottom、top 等,这个参数就是第一个参数中 view 所要进行的约束的位置relatedBy: 是一个NSLayoutRelation枚举,他的枚举值有 lessThanOrEqual(小于等于)、equal(等于)、 greaterThanOrEqual(大于等于)。 这个参数是用来指定 view1和接下来那个参数 view2两个视图之 间的约束关系的toItem: 和第一个参数一样,这个主要就是来和第一个 view 做参照的那个 视图attribute: 和第二个参数一样,是来表示第一个视图对第二个视图的 参考位置 ,上下左右 还是 center等multiplier: 乘数的意思,CGFloat类型。是来计算两个视图之间位置关系的 一个重要因素constant: 常数, CGFloat类型。也是计算两个视图位置关系的重要因素
      这几个参数所构造出来的NSLayoutConstraint实例,添加到视图之后的位置关系到底是怎样的呢,可以用下面 这个公式 来计算得出:
      item的attribute relatedBy toItem的attribute * multiplier + constant
      简化之后就是:
       A 视图    =      B视图  *  multiplier   +   constant;
      公式中的等于号可以根据 relatedBy 参数来变为 >= 或者 <=
    • NSLayoutAttribute布局属性

      NSLayoutAttribute,对象的可视表示的一部分,应该用于获取约束的值。
      typedef NS_ENUM(NSInteger, NSLayoutAttribute) {
          //对象对齐矩形的左侧
          NSLayoutAttributeLeft = 1,
          //对象对齐矩形的右侧
          NSLayoutAttributeRight,
          //对象对齐矩形的顶部
          NSLayoutAttributeTop,
          //对象对齐矩形的底部
          NSLayoutAttributeBottom,
          //对象对齐矩形的前缘
          NSLayoutAttributeLeading,
          //对象对齐矩形的后缘
          NSLayoutAttributeTrailing,
          //对象对齐矩形的宽度
          NSLayoutAttributeWidth,
          //对象对齐矩形的高度
          NSLayoutAttributeHeight,
          //沿对象对齐矩形x轴的中心
          NSLayoutAttributeCenterX,
          //沿对象对齐矩形的y轴的中心
          NSLayoutAttributeCenterY,
          //对象的基线。对于具有多行文本的对象,这是最下面一行文本的基线。
          NSLayoutAttributeLastBaseline,
      #if TARGET_OS_IPHONE
          NSLayoutAttributeBaseline NS_SWIFT_UNAVAILABLE("Use 'lastBaseline' instead") = NSLayoutAttributeLastBaseline,
      #else
          NSLayoutAttributeBaseline = NSLayoutAttributeLastBaseline,
      #endif
          //对象的基线。对于具有多行文本的对象,这是最上面一行文本的基线
          NSLayoutAttributeFirstBaseline API_AVAILABLE(macos(10.11), ios(8.0)),
      
      #if TARGET_OS_IPHONE
          //对象的左边距。对于UIView对象,页边距由其layoutMargins属性定义
          NSLayoutAttributeLeftMargin API_AVAILABLE(ios(8.0)),
          //对象的右边距。对于UIView对象,页边距由其layoutMargins属性定义
          NSLayoutAttributeRightMargin API_AVAILABLE(ios(8.0)),
          //对象的上边距。对于UIView对象,页边距由其layoutMargins属性定义。
          NSLayoutAttributeTopMargin API_AVAILABLE(ios(8.0)),
          //对象的下边距。对于UIView对象,页边距由其layoutMargins属性定义。
          NSLayoutAttributeBottomMargin API_AVAILABLE(ios(8.0)),
          //对象的前缘边距。对于UIView对象,页边距由其layoutMargins属性定义
          NSLayoutAttributeLeadingMargin API_AVAILABLE(ios(8.0)),
          //对象的后缘边距。对于UIView对象,页边距由其layoutMargins属性定义
          NSLayoutAttributeTrailingMargin API_AVAILABLE(ios(8.0)),
          //对象左右边距之间沿x轴的中心。对于UIView对象,页边距由其layoutMargins属性定义。
          NSLayoutAttributeCenterXWithinMargins API_AVAILABLE(ios(8.0)),
          //对象上下边距之间沿y轴的中心。对于UIView对象,页边距由其layoutMargins属性定义。
          NSLayoutAttributeCenterYWithinMargins API_AVAILABLE(ios(8.0)),
      #endif
          //占位符值,用于指示约束的第二项和第二个属性在任何计算中都不使用。创建将常量指定给属性的约束时,请使用此值。
          //例如,item1.height>=40。如果约束只有一个项,请将第二项设置为nil,并将第二个属性设置为NSLayoutAttributeNotAnAttribute
          NSLayoutAttributeNotAnAttribute = 0
      };
    • NSLayoutRelation布局关系

      typedef NS_ENUM(NSInteger, NSLayoutRelation) {
          //约束要求第一个属性小于或等于修改后的第二个属性。
          NSLayoutRelationLessThanOrEqual = -1,
          //约束要求第一个属性与修改后的第二个属性完全相等。
          NSLayoutRelationEqual = 0,
          //约束要求第一个属性大于或等于修改后的第二个属性。
          NSLayoutRelationGreaterThanOrEqual = 1,
      };
  • 使用第三方开源框架MasonrySnapKit

    • Masonry

      __weak typeof (self) weakSelf = self;
      [centerView mas_makeConstraints:^(MASConstraintMaker *make) {
          make.size.mas_equalTo(CGSizeMake(120, 120));
          make.center.equalTo(weakSelf.view);
      }];
    • SnapKit

      let centerView:UIView = UIView.init()
      view.addSubview(centerView)
      centerView.backgroundColor = UIColor.red
      centerView.snp.makeConstraints { (make) in
         make.width.equalTo(120)
         make.height.equalTo(120)
         make.center.equalTo(view)
      }
  • 使用iOS 9之后Apple提供的NSLayoutAnchor

    let centerView:UIView = UIView.init()
    view.addSubview(centerView)
    centerView.backgroundColor = UIColor.red
    centerView.translatesAutoresizingMaskIntoConstraints = false
    centerView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true
    centerView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true
    centerView.widthAnchor.constraint(equalToConstant: 120).isActive = true
    centerView.heightAnchor.constraint(equalToConstant: 120).isActive = true
    • NSLayoutAnchor常用属性

      NSLayoutAnchor是对AutoLayout创建约束的补充,核心还是NSLayoutConstraint,可以避免过长创建约束代码。NSLayoutAnchor可以理解为约束描边,通过视图之间的边关系和X、Y轴关系,以及定义自身宽高来创建约束。

      • 四边关系

        leadingAnchor   前边锚(与trailingAnchor成对使用)

        trailingAnchor   后边锚

        leftAnchor        左边锚(与rightAnchor成对使用)

        rightAnchor      右边锚

        topAnchor       上边锚

        bottomAnchor  下边锚

      • 大小关系

        widthAnchor    宽度约束

        heightAnchor   高度约束

      • 中心点关系

        centerXAnchor  X轴对齐关系

        centerYAnchor  Y轴对齐关系

      • 基准线

        firstBaselineAnchor 文本首行基准线

        lastBaselineAnchor 文本最后一行基准线

    • UILayoutGuide

      UILayoutGuide用于辅助添加约束,它的作用就像一个透明的View,具备约束参考,但是不会渲染。假如我们想让三个view水平对齐,且中间间距相等。我们就可以使用UILayoutGuide辅助实现。  

  通过上面的代码对比,使用frame手写布局只要几行代码就搞定了,使用NSLayoutConstraint语法最复杂的,尤其是NSLayoutConstraint语法要用30多行代码才能是想同样的效果,代码行数越多出错的概率也就成正比上升,所以这就是很多开发者不愿使
Auto Layout(或者说不愿意使用系统提供API来实现)的原因之一吧。

    如果你的App要兼容iOS 9以下的各个版本,建议使用Masonry,如果只兼容iOS 9以上的版本,建议使用SnapKit或者系统提供的NSLayoutAnchor API,毕竟Masonry这个库已经2年没有更新了。

  在这里我推荐优先使用NSLayoutAnchor,第三方的开源库随时都面临着一些问题:

  • iOS 系统版本的更新造成的适配和兼容问题,如果是开源代码要等到苹果发布新版本,代码的作者再做兼容和适配
  • 代码的作者停止更新这些代码了,这对我们开发者来说就很被动了,我们要么自己修改这些代码,要么选择更新的开源代码
  • 使用系统库可在打包时可以减少包大小

3、Auto Layout的生命周期

  • APP启动后,随着RunLoop的运行,系统在其内部监听着约束变化(Constraints Change):如激活或失效约束、修改优先级、修改常量值等任意造成约束方程式修改的操作,甚至是直接添加或删除视图等操作,都可以导致约束发生变化。

  • 在接收到布局变化后,Layout Engine会根据变化的约束重新计算布局,并将需要更新布局的视图进行标记(对其父视图调用setNeedsLayout方法),之后便进入延迟布局阶段(Deffered Layout Pass)。

    注意

    在进入延迟布局阶段之前,Layout Engine已经将更新的约束计算完毕并将视图的新frame求出。但并不在此时更新视图。

  • 延迟布局阶段:此阶段的主要作用是将错误位置的视图重新定位(Reposition misplaced views)。其在视图层级中执行,分为两步:
    • 更新约束:从下往上(子视图到父视图),依次遍历视图层级,调用View的updateConstraints方法(或ViewController的updateViewConstraints方法)来更新约束(你可以在此覆盖本方法来设置自定义约束,且在此设置时,执行效率最高。记得最后调用父类实现)。

    • 给视图及子视图重新设定位置(给view的frame赋值):从上到下依次调用View的layoutSubViews方法(或ViewController的viewLayoutSubViews方法),从Layout Engine中取出预算好的frame进行赋值(你可以覆盖此方法实现自定义布局,不过此刻不是稳态,需要在适合时候调用父类实现)。

  延迟布局阶段的触发条件

调用方法结果
setNeedsUpdateConstraints 下一次loop执行updateConstraints
updateConstraintsIfNeeded 立即执行updateConstraints
setNeedsLayout 下一次loop执行layoutSubViews
layoutIfNeeded 立即执行layoutSubViews
setNeedDisplay 下一次loop执行draw

4、Auto Layout几个更新约束的方法

  • setNeedsLayout: 告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews

  • layoutIfNeeded:  告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。

  • layoutSubviews: 更新子View约束

  • setNeedsUpdateConstraints: 需要更新约束,但是不会立刻开始

  • updateConstraintsIfNeeded: 立刻更新约束

  • updateConstraints更新View约束

5、NSLayoutAnchor使用注意事项

  • 1、在使用NSLayoutAnchor为视图添加约束时一定要先把translatesAutoresizingMaskIntoConstraints设置false

    centerView.translatesAutoresizingMaskIntoConstraints = false
  • 2、在使用safeAreaLayoutGuide适配iPhone X 等机型时要对iOS 11之前的系统做适配,否则会导致低版本系统上程序Crash

    if #available(iOS 11.0, *) {
       tableView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
    } else {
       tableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
    }
  • 3、设置约束后要将其激活,即设置isActivetrue

    let centerX: NSLayoutConstraint = centerView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0)
    centerX.isActive = true
  • 4、leadingAnchor 不要和 leftAnchor混用

    centerView.leadingAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
    centerView.leftAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true

    以上2种写法,在编译时不会出现任何问题,但是在运行时就会报错,并会导致程序Crash,官方的说法是:

    While the NSLayoutAnchor class provides additional type checking, it is still possible to create 
    invalid constraints. For example, the compiler allows you to constrain one view’s leadingAnchor
     with another view’s leftAnchor, since they are both NSLayoutXAxisAnchor instances. However, 
    Auto Layout does not allow constraints that mix leading and trailing attributes with left or right 
    attributes. As a result, this constraint crashes at runtime.

    同理,trailingAnchorrightAnchor也不能混用。

  • 5、如何刷新某个约束

    如我要修改一个UIView的宽度: 通过代码添加约束,可把UIView的宽度设置类属性,然后在需要的地方修改constant的参数,然后在刷新约束即可,
    代码如下:
    var centerView: UIView! 
    var centerWidth: NSLayoutConstraint! self.centerView = UIView.init() view.addSubview(self.centerView) self.centerView.backgroundColor = UIColor.red self.centerView.translatesAutoresizingMaskIntoConstraints = false self.centerView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true self.centerView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true self.centerWidth = self.centerView.widthAnchor.constraint(equalToConstant: 120) self.centerWidth.isActive = true self.centerView.heightAnchor.constraint(equalToConstant: 120).isActive = true self.centerWidth.constant = 250 weak var weakSelf = self UIView.animate(withDuration: 0.35, animations: { weakSelf?.centerView.superview?.layoutIfNeeded() }) { (finished) in }

    效果如下:

    如果是xib或者storyboard,那就更简单了,直接摁住键盘control键,拖到对应的类里,然后在需要的地方修改约束并刷新即可。操作如下:

  • 6、设置宽高比

    在开发中,我们会遇到一些需求要求根据UIView的宽高比来设置约束,如一般情况下显示视频的宽高比是16:9,通过代码设置宽高比如下:

    centerView.heightAnchor.constraint(equalToConstant: 90).isActive = true
    centerView.widthAnchor.constraint(equalTo: centerView.heightAnchor, multiplier: 16 / 9).isActive = true

6、Auto Layout自适应UITableViewCell高度使用

  • 1、使用rowHeight设置高度

    一般情况下,如果UITableView的每个Cell高度是固定的我们可以直接指定一个值即可,如果没有设置UITableView的高度,系统会默认设置rowHeight高度是44。

    tableview.rowHeight = 44;

    也可以通过UITableViewDelegate的代理来设置UItableView的高度。

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }

    如果通过手动计算每个UItableViewCell的高度,也在这个代理中实现,通过计算返回每个UItableViewCell的高度。

  • 2、使用estimatedRowHeight设置高度

    UItableView继承自UIScrollView,UIScrollView的滚动需要设置其contentSize后,然后根据自身的bounds、contentInset、contentOffset等属性来计算出可滚动的长度。而UITableView在初始化时并不知道这些参数,只有在设置了delegatedataSource之后,根据创建的UITableViewCell的个数和加载的UITableViewCell的高度之后才能算出可滚动的长度。

    在使用Auto Layout自适应UITableViewCell高度时应提前设置一个估算值,当然这个估算值越接近真实值越好。

    tableView.rowHeight = UITableView.automaticDimension
    tableView.estimatedRowHeight = 200
    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
       return 200    
    }

    如上图所示:这个界面就是用Auto Layout + estimatedRowHeight完成自适应高度的,在添加约束时要按照从上到下的书讯设置每一个UIView的顶部(top)到上一个的视图底部的(bottom)距离,同时要计算UITableViewCell内部所有控件的高度。那么问题来了,用户发布的内容详情没有得到数据之前时没办法算出其高度的,此处可以先给内容文字UILabel设置一个默认高度,然后让其根据内容填充自动计算高度:

    topicInfoLab.heightAnchor.constraint(greaterThanOrEqualToConstant: 20).isActive = true;
    topicInfoLab.font = UIFont.init(name: "Montserrat-SemiBold", size: 12)
    topicInfoLab.numberOfLines = 0

    如果用户发布内容没有图片,直接设置发布内容UILabel距离UITableView距离底部的约束距离即可;

    detailsLab.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -8).isActive = true

    如果用户发布的内容有图片,那么在计算出每张图片的位置和大小之后,一定要给最后一张图片设置距离UItableViewCell底部(bottom)的约束距离。

    for(idx, obj) in imageArray.enumerated() {
        //.....计算图片的大小和位置
        if idx == imageArray.count - 1 {
            //设置最后一张图片距离底部的约束
            photo.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -8).isActive = true
        }
    }

    实现思路如上图所示,具体实现的请看代码

7、 Compression Resistance PriorityHugging Priority使用

  Compression Resistance PriorityHugging Priority在实际使用中往往配合使用,分别处理在同义水平线上多个view之间内容过少和内容过多而造成的互相压挤的情况。

  Hugging Priority的意思就是自包裹的优先级,优先级越高,则优先将尺寸按照控件的内容进行填充。

  Compression Resistance Priority,意思是说当不够显示内容时,根据这个优先级进行切割。优先级越低,越容易被切掉。

ContentHuggingPriority表示当前的UIView的内容不想被拉伸
ContentCompressionResistancePriority 表示当前的UIView的内容不想被收缩
默认情况下: HuggingPriority = 250 默认情况下: CompressionResistancePriority = 750

  如设置2个UILabel的拉伸优先级可使用代码:

fristLab.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal)
secondLab.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)

8、总结

    本文主要分享了苹果Auto Layout的几种实现方法和注意事项,对于Auto Layout在实际开发中的使用是采用纯代码、还是xib + 代码,还是storyboard + 代码,还是xib + storyboard + 代码的方式实现,主要看团队的要求、个人的习惯,以及App的繁琐程度。 对于Auto Layout在视图上的使用,个人建议如果UI比较简单或者单一的界面可使用Auto Layout,如果UI的操作或刷新很复杂的界面,建议还是frame + 手动布局的方式。


本文demo,请戳这里

友情链接:

深入剖析Auto Layout,分析iOS各版本新增特性

Auto Layout 是怎么进行自动布局的,性能如何?

Apple Developer High Performance Auto Layout

Apple Develope NSLayoutConstraint

WWDC 2018 What's New in Cocoa Touch

 

posted on 2021-09-07 13:26  梁飞宇  阅读(1291)  评论(0)    收藏  举报