美文网首页
SnapKit 的那些事

SnapKit 的那些事

作者: Lin__Chuan | 来源:发表于2018-12-09 02:57 被阅读39次

    SnapKit 是一款可在 iOS 和 OS X 上轻松实现 Auto Layout 的 DSL , 用 Swift 实现.
    与之对应, Masonary 用 Objective-C 实现, 两个框架的作者均是 SnapKit 团队

    这里有几点需要注意: (下面说的 AutoLayout 均为官方的布局技术, 不包括第三方的布局框架)

    Auto Layout 的由来及使用

    Auto Layout 是 Apple 提供的用于布局App页面的技术, 可以通过 Interface Builder 或者 纯代码 使用.

    1. 通过 StoryBoard / xib 可以轻松设计界面, 拖线布局页面, 适用于业务逻辑不是很复杂的项目.
    2. 通过 纯代码 使用 AutoLayout, 由于Apple的原始布局API比较繁琐, 所以诞生了SnapKit, Masonary 这两种框架.

    下面是一个例子, view1是子页面, superView 是其父页面, 我们需要设置 view1 的内边距均为10.

    由上面可以明显看出, SnapKit 框架通过包装原生布局 API , 极大的简化了API 的调用.

    与 AutoLayout 相对的布局方式是 FrameLayout.

    • FrameLayout 的优点是固定精确的坐标, 所以很多不需要计算, 性能上比较好, 缺点是对于复杂的界面布局, 比较繁琐.
    • AutoLayout 的优点是对于复杂界面比较容易处理, 但由于需要对控件的 frame 进行更多的计算, 性能较差.
    • Auto Layout 其实就是对 Cassowary 算法的一种实现, 在布局时可以指定一系列的约束, 比如宽度, 高度, 边距等. 只有知道每一个 View 的 x, y, width, height, 才能确定它的 frame, 最终渲染到界面上. 对于每一个约束, Cassowary 的布局算法将其转化为简单的线性等式或不等式, 通过求解来算出 View 的 frame.

    DSL 的由来及使用

    DSL (Domain Specific Language), 特定领域下的语言, 即为了解决某些特定场景下的任务而专门设计的语言. 与 DSL 相对应的是 GPL (General programming language), 通用编程语言, 比如 C/C++/Objective-C/Swift 等.

    DSL 的例子:

    • 正则表达式: 通过一些规定好的符号和规则, 使用正则表达式解释器来实现字符串的匹配.
    • SQL: 通过 create select insert 等 SQL 语句, 利用底层数据库框架, 实现对数据库进行增删改查等操作.
    • HTML&CSS: 通过类似 XML 或者 .{}一样的字符规则, 最终都会被浏览器内核转变成 Dom 树, 最终渲染到 WebView 上.

    离开了为某一 DSL 专门开发的语言环境或者代码框架, DSL 是无法运行的.

    SnapKit 源码分析

    有几点需要说明

    • 在 iOS 中 ConstraintView 实际就是 UIView.
    • ConstraintViewDSL 在初始化时持有传入的 ConstraintView, 并且通过调用 makeConstraints 设置约束.
    • ConstraintViewDSL 继承自 ConstraintAttributesDSL , ConstraintAttributesDSL 中定义的是 iOS8 中出现的属性, 比如 leftMargin, topMargin 等. ConstraintBasicAttributesDSL 中定义的是一开始就有的那些属性.
    • ConstraintMaker 是设置约束的核心. 其中 ConstraintDescription 包含有所有约束的相关信息.
    • ConstraintAttributes 用来描述约束属性, 继承自 OptionSet, 可以将多个选项组成值(位域). 还继承自 ExpressibleByIntegerLiteral, 可以使用整形数据生成选项的集合.
    • ConstraintMakerExtendable 继承自 ConstraintMakerRelatable, 后者定义了 equalTo 等相关方法, 用于设置约束值. 并且 equalTo 返回一个 ConstraintMakerEditable 的实例.
    • ConstraintMakerEditable 主要用来设置约束的 offset 和 inset 还有 multipliedBy 和 dividedBy 函数, ConstraintMakerPriortizable 用来设置优先级, ConstraintMakerFinalizable 中包含所有的约束信息.

    SnapKit 的源码启示

    • 条件编译, 命别名

    #if, #else, #endif, 可以用来控制编译流程和内容, os() 可以检测系统平台.
    typealias, 可以为类起别名, 也可以实现跨平台

    #if os(iOS) || os(tvOS)
        import UIKit
        #if swift(>=4.2)
            typealias LCLayoutRelation = NSLayoutConstraint.Relation
            typealias LCLayoutAttribute = NSLayoutConstraint.Attribute
        #else
            typealias LCLayoutRelation = NSLayoutRelation
            typealias LCLayoutAttribute = NSLayoutAttribute
        #endif
        typealias LCLayoutPriority = UILayoutPriority
    #else
        import AppKit
        typealias LCLayoutRelation = NSLayoutConstraint.Relation
        typealias LCLayoutAttribute = NSLayoutConstraint.Attribute
        typealias LCLayoutPriority = NSLayoutConstraint.Priority
    #endif
    
    • @available

    @available 用于函数、方法、类或协议的前面,表明平台和操作系统适用性.
    * 表示全平台, deprecated:3.0, 表示版本号3.0开始过时, message:"", 表示消息内容.

    @available(*, deprecated:3.0, message:"Use newer snp.* syntax.")
    public var snp_left: ConstraintItem { return self.snp.left }
    
    @available(iOS 9.0, *)  表示 iOS 9.0 开始适用
    
    • #file#line 是系统为我们提供的编译符号, 用来当前方法的文件名和行号. 配合 fatalError()guard, 可以直接提示错误信息
     fatalError("Cannot constraint to multiple non identical attributes. (\(file), \(line))");
    
    • 通过这个协议来扩展下只支持的类型,达到限制类型的功能
    public protocol LCConstraintRelatableTarget {
    }
    
    extension Int: LCConstraintRelatableTarget {
    }
    
    extension UInt: LCConstraintRelatableTarget {
    }
    
    • Constraint 类是 NSLayoutConstraint 类的包装类, 其中包含了约束的所有参数, 更新和激活约束的相关方法. ConstraintDescription 类是 Constraint 类的包装类, 包含约束的所有信息.

    • 如果要限制一个类不能被其他类限制, 可以加上关键字 final.

    • ConstraintAttributes 继承自 OptionSet, 可以将多个选项组合成一个值, 而不是类似枚举, 只能选择一个. 自定义运算符, 简化方法调用.

    internal func + (left: LCConstraintAttributes,
                     right: LCConstraintAttributes) -> LCConstraintAttributes {
        return left.union(right)
    }
    
    internal func +=(left: inout LCConstraintAttributes,
                     right: LCConstraintAttributes) {
        left.formUnion(right)
    }
    
    internal func -=(left: inout LCConstraintAttributes,
                     right: LCConstraintAttributes) {
        left.subtract(right)
    }
    
    internal func ==(left: LCConstraintAttributes,
                     right: LCConstraintAttributes) -> Bool {
        return left.rawValue == right.rawValue
    }
    
    • 通过继承 CustomStringConvertible, 修改对象的 description 属性, 自定义对象的描述. 类似下面这样.
    struct Person: CustomStringConvertible {
        var name: String?
        
        var description: String {
            var desc = "<"
            desc += self.name ?? "lili"
            desc += ">"
            
            return desc
        }
    }
    

    参考
    从 Auto Layout 的布局算法谈性能
    深入理解 Autolayout 与列表性能 -- 背锅的 Cassowary 和偷懒的 CPU
    谈谈 DSL 以及 DSL 的应用
    动态界面:DSL&布局引擎

    相关文章

      网友评论

          本文标题:SnapKit 的那些事

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