美文网首页Swift - basis
Swift UI纯代码自动布局-VFL

Swift UI纯代码自动布局-VFL

作者: 张小西的BUG | 来源:发表于2019-12-12 21:20 被阅读0次
VFL全称Visual Format Language,可视化的布局。一直感觉iOS布局框架五花八门,大概都是为了解决苹果自动布局鸡肋的问题,虽然苹果推出了xib但还是不喜欢用,不仅是因为代码维护和合并的问题,主要是因为自己比较喜欢纯代码布局。
在OC里面VFL的布局代码依旧是很麻烦当时主要用的还是Masonry,到了Swift里面就简化了很多以至于我不再使用第三方布局框架,之后的项目全部都是用的VFL。而使用自动布局觉得最方便之处,1:子控件可以去撑父试图,避开繁琐frame的计算所有子控件根据内容自动撑父试图,2:tableViewCell可以根据子试图内容动态计算高度,不需要设置固定高度。
当我回头看以前用第三框架布局的代码的时候我发现,真的很难维护,因为比较零散,所以的约束是以子控件为单位写的,全部都是block回调里写的约束,很臃肿,不够直观一眼看不到子控件之间的约束,但是当时在写的时候很清晰。VFL是以父试图为单位写的约束,一行代码可以写一个方向的约束,子控件之间的约束一眼就能看到,后来就彻底使用VFL。

后来在面试中我也会问下别人会使用VFL布局吗,当然不作为考核只是仅仅的想了解下,有了第三框架会不会再去学苹果的布局框架。最后发现很少有人会去使用,基本都是用的Masonry和SnapKit或者xib,都说写法简单,但是我想说的VFL也很简单也有很多高阶写法,维护起来更方便。

先看下主要布局的API,都是UIKit框架里面的API

 @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])

最常用的是 open func addConstraints(_ constraints: [NSLayoutConstraint])其中[NSLayoutConstraint]open class func constraints(withVisualFormat format: String, options opts: NSLayoutConstraint.FormatOptions = [], metrics: [String : Any]?, views: [String : Any]) -> [NSLayoutConstraint]API创建,所以我们常见的写法就是contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-60-[list][closeButton(24)]-16-|", options: [], metrics: nil, views: vd))

  • V表示垂直方向,|表示父试图上边距(左边的|)和下边距(右边的|),-60-表示控件之间的间距, []内部表示具体的子控件。
  • options: 表示布局的对齐方式,通常指的某一个方向的子控件,比如垂直方向,左边全部对齐,那么这一组约束options就为[.alignAllLeading],具体可以对齐方式可以去看苹果的文档
  • metrics: 基本都可以传nil
  • views是一个[String: UIView]类型的map,value表示具体的子控件,format字符串中[]内的子控件通过key来取到具体的子控件。

如果对VFL了解的话,那么上面说的都是废话,可以参考苹果文档去看,但是苹果文档上写的也很复杂,主要是布局情况太多,举了各种例子。但是我想说的是真的很简单,但是想熟练运用还是很难的,经常会出现约束冲突,或者是一个小的错误导致整个界面显示不出来。但是不管怎么样,先用起来才能学的更快,有了错误再解决。

  • 权重和子控件抗压缩和抗拉伸问题
    在自动布局中还有一个问题不得不提下,约束权重,抗拉伸和抗压缩。权重啥时候会出现呢,父控件在某一个方向上长度固定,哪个子控件就会被拉伸或者压缩,这时候就要设置约束权重,和子控件抗拉伸或者抗压缩的权重。设置权重的API,具体参数和使用可以去参考官方的注释,swift使用是很简单。当然VFL在块是做的比较不错的,要是直接写约束会稍微麻烦点,总之遇到控件没有按照预期拉伸,就是这方面原因,在次就不举具体例子了,作为一个猿自己需要去学习和踩坑。

    func setContentHuggingPriority(_ priority: UILayoutPriority, for axis: NSLayoutConstraint.Axis)
    func setContentCompressionResistancePriority(_ priority: UILayoutPriority, for axis: NSLayoutConstraint.Axis)

  • 个人感觉VFL的不足
    先说下自己对VFL觉得不够满意的地方,当然也可能是我才疏学浅。第一点就是不能通过VFL直接设置子控件和父控件的居中对齐方式,当然这也不是什么大问题,直接写约束也能解决。第二点就是安全区的问题,这个问题直接拉低了VFL的档次,没有参数设置适配安全区,必须写约束去适配刘海。

  • 自动布局UIScroller中遇到的问题
    通常大多数我们都是会用子控件去撑父试图的大小。但是有一个控件比较特殊UIScroller,如果父试图是UIScroller,那么子控件撑的是它的contentsize,所以能否滚动,子控件说了算,还需要说一点就是既然撑的是contentsize,如果scroller是上下滚动那么子控件的水平右边距不能以scroller的右边约束对齐,除非子控件的宽度是固定值用来撑scroller的content.width,不然的话需要直接写约束来设置子控件的宽度的最大值(通常和scroller的宽度相同)。自从使用了自动布局,再也不用计算contentsize了。还有一种比较麻烦的问题可以用UISCroller解决,我们经常会遇到某些控件没有数据时是不显示的,连高度都没有,这时候我们就可以将这些控件写到UISsroller上面,通过设置UIScroller的高度来达到隐藏控件的目的,先说下,使用layout如果不设置高度,控件也会显示在父试图上,因为控件上有东西显示就会哦撑控件,但是UIScroller不会显示,因为它的子控件是撑的是它的contentsize。当然还可以用一种比较low的临时解决方案,强行设置父控件高度约束为0,然后隐藏,界面没问题,但是layout会报约束错误。

  • tableView中使用自动布局遇到的问题
    再一个常用的地方tableView,如果cell能被子控件撑满高度,那么在代理里面是不需要返回具体的高度的,只需要返回UITableView.automaticDimension,但是要设置tableViewCell的预计高度estimatedRowHeight,代理回调也可以返回。这样能很好的实现不等高的cell,不需要根据内容去计算高度了,会自动帮你计算高度。不仅cell还有sectionHead/sectionFoot都可以通过自动布局来自动计算不同的高度,强烈建议下,不管sectionView多么简单,都希望使用系统提供的UITableViewHeaderFooterView来复用。提个小问题,iOS11后面tableView出现了很多小毛病,基本都是estimatedRowHeight导致的,如果直接使用自动布局是不会有这些小问题。当然tableHeaderView/tableFootView也可以使用自动布局,可以不计算高度,iOS11之后可以很完美的支持,但是呢iOS11之前需要对header/footer特殊处理下。但是个人建议,如果header/footer高度是固定的不需要根据内容动态填充,建议直接设置frame。这里面的坑有点多,需要大胆去尝试使用,踩过后估计就以后都不想计算高度了。还有就是导航栏View也是在iOS11后支持layout的iOS11之前也是要特殊处理。再此就只抛出问题,不写解决方案了,这些坑都踩过,不是很难。

  • 自动布局界面使用UIView动画
    布局使用UIViw的动画,如果用Frame布局,动画很简单改变Frame就行了,但是layout是不行的,最常用的就是二种实现方法,首先把需要做动画的View需要改变的约束单独写出来,直接修改约束的constant,还有一种就是把动画的起始位置和结束位置的需要改变的约束约束单独写出来,来设置约束是否生效来完成动画。不管用哪种方式,动画过程中都需要使用layoutIfNeeded()更新约束。如果使用复杂的动画,使用CoreAnimation框架,结合CAShapeLayer和UIBezierPath基本都可以实现。

  • 自动布局中获取控件的frame
    使用自动布局,如果想得到控件的frame怎么办?如果自动布局的话,想拿到控件的frame通常都是在layoutSubviews或者draw方法里面,但是有时后我们不想重写方法里面拿到,还可以直接调用控件父试图的layoutIfNeeded()然后就可以拿到控件的frame,这个方法就是更新所有约束。自动布局也是一个多元一次方程,通过复杂的计算来的得出具体的frame。layoutIfNeeded()是更新约束的方法,有时候需要合理的去调用,不然对于复杂的界面有可能会出现奇怪的问题。

写在最后面:只是大概罗列了使用layout中可能会遇到的问题,没有具体去说怎么使用VFL,因为这只是一个布局的方式,个人的喜好,想去了解也是很简单,只是我个人推荐使用VFL为主做自动布局,自己也使用了三年多。UI玩的转对一个iOS开发猿来说,是最基本的,自己在开发中喜欢封装小的UI组件,任重而道远。

相关文章

网友评论

    本文标题:Swift UI纯代码自动布局-VFL

    本文链接:https://www.haomeiwen.com/subject/ppciwctx.html