美文网首页
如何在10分钟内编写一个简易的 Snapkit

如何在10分钟内编写一个简易的 Snapkit

作者: yww | 来源:发表于2017-08-31 16:54 被阅读51次

    虽然重复造轮子是不好的, 不过通过阅读其他大神的代码, 自己再仿造一个出来, 也是很有学习意义的.
    通过本文, 你可以实现一个功能简陋的 snapkit, 代码一共只有104行, 不过实现的效果还是不错的

    myLabel.mysnp.make { (maker) in
     maker.left.equalTo(bg.mysnp.left).offset(8)
     maker.top.equalTo(bg.mysnp.top).offset(8)
    }
    

    不过功能上嘛


    真正的 snapkit, 我写的 snapkit

    废话不说, 先来介绍一下主要的四个模块

    UIView 扩展

    extension UIView {
        var mysnp: MyConstraintDSL {
            return MyConstraintDSL(view: self)
        }
    }
    
    

    通过这个扩展, 可以获取到一个生成器. 正在的 snapkit 并不是简单的扩展 UIview, 而是根据平台平台不同, 扩展的 UIView 或是 NSView.

    // 真正的 snapkit 实现
    #if os(iOS) || os(tvOS)
        import UIKit
    #else
        import AppKit
    #endif
    
    #if os(iOS) || os(tvOS)
        public typealias ConstraintView = UIView
    #else
        public typealias ConstraintView = NSView
    #endif
    
    public extension ConstraintView {
        public var snp: ConstraintViewDSL {
            return ConstraintViewDSL(view: self)
        }
    }
    

    MyConstraintDSL 类实现

    通过上面的扩展, 我们调用了MyConstraintDSL(view: self), 创建了一个 dsl 类, 这个类主要是实现了一些布局属性, 例如 top, left 等

    class MyConstraintDSL {
        fileprivate weak var view:UIView!
        init(view: UIView) {
            self.view = view
        }
        func make(_ closure: (MyConstraintMaker)->()) {
            let maker = MyConstraintMaker(view: self.view)
            maker.make(closure)
        }
    }
    extension MyConstraintDSL {
        var top: MyConstraintItem {
            let item = MyConstraintItem(prop: .top, view: self.view)
            return item
        }
        var left: MyConstraintItem {
            let item = MyConstraintItem(prop: .left, view: self.view)
            return item
        }
        // 其他属性类似
    }
    

    MyConstraintMaker 类实现

    这个类主要就是负责创建使用 maker.left.equalTo(xxx) 创建布局, 并在稍后生成实际的NSLayoutConstraint

    class MyConstraintMaker {
        fileprivate weak var view:UIView!
        fileprivate var constants = [MyConstraintItem]()
        init(view: UIView) {
            self.view = view
        }
        func make(_ closure: (MyConstraintMaker)->()) {
            self.view.translatesAutoresizingMaskIntoConstraints = false
            closure(self)
            for constant in self.constants {
                constant.getLayoutConstraint()?.isActive = true
            }
        }
    }
    extension MyConstraintMaker {
        var top: MyConstraintItem {
            let item = MyConstraintItem(prop: .top, view: self.view)
            self.constants.append(item)
            return item
        }
        var left: MyConstraintItem {
            let item = MyConstraintItem(prop: .left, view: self.view)
            self.constants.append(item)
            return item
        }
        // 其他属性类似稍后
    }
    

    MyConstraintItem 实现

    这个类是保存布局信息, 以方便稍后根据这些信息生成 NSLayoutConstraint

    class MyConstraintItem {
        fileprivate weak var view: UIView?
        fileprivate var prop: NSLayoutAttribute
        fileprivate var relation: NSLayoutRelation = .equal
        fileprivate var anotherItem: MyConstraintItem?
        fileprivate var offset: CGFloat = 0
        
        init(prop: NSLayoutAttribute, view: UIView? = nil) {
            self.prop = prop
            self.view = view
        }
        @discardableResult
        func equalTo(_ anotherItem: MyConstraintItem) -> MyConstraintItem{
            self.relation = .equal
            self.anotherItem = anotherItem
            return self
        }
        @discardableResult
        func equalTo(_ value: CGFloat) -> MyConstraintItem{
            self.relation = .equal
            self.anotherItem = MyConstraintItem(prop: self.prop, view: nil)
            self.anotherItem?.offset = 0
            return self
        }
        @discardableResult
        func offset(_ offset: CGFloat) -> MyConstraintItem {
            self.offset = offset
            return self
        }
        fileprivate func getLayoutConstraint() -> NSLayoutConstraint? {
            guard self.anotherItem != nil else {
                return nil
            }
            // 如果自己是一个常量类型约束, 那么就无法只靠自己生NSLayoutConstraint
            guard self.view != nil else {
                return nil
            }
            return NSLayoutConstraint(item: self.view!, attribute: self.prop, relatedBy: self.relation, toItem: self.anotherItem!.view, attribute: self.anotherItem!.prop, multiplier: 1, constant: self.offset)
        }
    }
    

    恭喜你完成了, 不过离实际的 snapkit 还是差距很远的, 例如, 我们这里只支持单个属性, 但是 snapkit 有可能 make 会生成多条约束, 我们这个就明显不足了.

    相关文章

      网友评论

          本文标题:如何在10分钟内编写一个简易的 Snapkit

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