PLUi - Swift 3 纯代码构造 UI 的函数式帮助类

帮助类 - PLUi

PLUi 是一个使 Swift 3 能够以闭包嵌套结构创建 UI 的工具类,用于彻底取代 Storyboard。

帮助类本体:PLUi.swift

//
//  PLUi.swift
//
//  Created by Plutonist on 16/10/15.
//  Copyright © 2016 Plutonist. All rights reserved.
//

import Foundation
import UIKit

struct UpdateConstraintsFn {

    let fn: (_ view: UIView, _ parent: UIView) -> Void
    let view: UIView
    let parentView: UIView
    
}


class PLUi {
    
    var didSetupConstraints = false
    
    var updateConstraintsFns = [UpdateConstraintsFn]()
    
    init(view: UIView, closure: (_ this: UIView, _ add: PLUiNode) -> Void) {
        let node = PLUiNode(view: view, ui: self)
        closure(view, node)
        view.setNeedsUpdateConstraints()
    }
    
    func updateConstraints() {
        if (!didSetupConstraints) {
            updateConstraintsFns.forEach { update in
                update.fn(update.view, update.parentView)
            }
            didSetupConstraints = true
        }
    }
    
}

class PLUiNode {
    
    let parentView: UIView
    let ui: PLUi
    var view: UIView?
    
    init(view: UIView, ui: PLUi) {
        self.parentView = view
        self.ui = ui
    }

    func add(_ initializer: () -> T, closure: (_ this: T, _ child: PLUiNode) -> Void) -> PLUiNode {
        return add(initializer(), closure: closure)
    }
    
    func add(_ newView: T, closure: (_ this: T, _ child: PLUiNode) -> Void) -> PLUiNode {
        let node = PLUiNode(view: newView, ui: ui)
        closure(newView, node)
        self.parentView.addSubview(newView)
        view = newView
        return self
    }
    
    func layout(_ update: @escaping (_ view: UIView, _ parent: UIView) -> Void) -> PLUiNode {
        if view != nil {
            ui.updateConstraintsFns.append(UpdateConstraintsFn(fn: update, view: view!, parentView: parentView))
        }
        return self
    }
    
}

使用方法

删除 Info.plist 中的:

Paste_Image.png

添加到 AppDelegate.swfit 的初始化方法中

self.window = UIWindow(frame: UIScreen.mainScreen().bounds)

let mainViewController = MainViewController()
let navigationController = UINavigationController(rootViewController: mainViewController);
navigationController.navigationBarHidden = true

self.window!.rootViewController = navigationController;

self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()

return true

基础使用方法 - 在 MainViewController.swift 中创建 UI


// 预先声明 PLUi 的对象属性,用于之后的更新布局
var ui: PLUi!

// 声明一个为空、示例用的 UIView 属性,用于给 PLUi 绑定 View 以及后续的使用
var blockOne: UIView!

// 更新布局
override func updateViewConstraints() {
    ui.updateConstraints()
    super.updateViewConstraints()
}

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 在此处进行 Ui 初始化
    ui = PLUi(view: view) { this, child in
        blockOne = child.add(newView: UIView()) { this, child in
            this.backgroundColor = UIColor.red

            // 这个位置是可以无限嵌套 child.add 的
        }.layout { this, parent in
            this.snp.makeConstraints { make in
                make.width.equalTo(parent.bounds.width)
                make.height.equalTo(64)
                make.top.equalTo(0)
            }
        }.view! as UIView
    }
}

通过 PLUi 类可以创建一个基于当前 ViewController 的 Ui 框架对象,用于填充子视图,是在每个 ViewController 中构建 Ui 所必须的。PLUi 构造方法会产生一个闭包,在闭包内可以通过 this 参数获取当前 ViewController 的 view 属性。

child 参数则是一个 PLUiNode 对象,我们可以通过这个对象在这个闭包中创建一个该 View 的子元素,.add 方法就是创建子元素的方法,它还是会返回一个 PLUiNode 对象,我们可以直接从这个对象中获取刚刚创建的子元素对应的 View 对象,亦或者是通过 .layout 方法产生一个闭包,用来放置 AutoLayout 相关的代码。

进阶使用方法 - 通过创建 BaseViewController.swift 来优化 ViewController 代码

import UIKit
import SnapKit

class BaseViewController: UIViewController {
    
    var ui: PLUi!
    
    override func updateViewConstraints() {
        ui.updateConstraints()
        super.updateViewConstraints()
    }
    
    func initUi() {}
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initUi()
    }
    
}

需要手动重载的两个方法中,initUi() 是用于放置 UI 生成相关代码的,viewDidLoad() 作用不变。

现在,ViewController 就可以改成以下的写法,显得更加精炼简洁
ViewControllerUIViewController 的继承需要改成 BaseViewController

var blockOne: UIView!

override func initUi() {
    ui = PLUi(view: view) { this, child in
        blockOne = child.add(newView: UIView()) { this, child in
            this.backgroundColor = UIColor.red
        }.layout { this, parent in
            this.snp.makeConstraints { make in
                make.width.equalTo(parent.bounds.width)
                make.height.equalTo(64)
                make.top.equalTo(0)
            }
        }.view! as UIView
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
}

进阶使用方法 - 在 TableViewCell 中使用 PLUi

此处就直接介绍如何通过创建 BaseTableViewCell 来使用 PLUi

import UIKit
import SnapKit

class BaseTableViewCell: UITableViewCell {
    
    var ui: PLUi!
    
    override func updateConstraints() {
        ui.updateConstraints()
        super.updateConstraints()
    }
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        initUi()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func initUi() {}
    
    func configure() {}
    
}

两个需要重载的类中,initUi() 和 ViewController 中的作用一样,用于放置 Ui 生成代码,configure() 其实各位可以自行发挥,我在目前的项目中是用于配置 cell 的内容。

使用场景中:

import UIKit
import SnapKit

class SitesTableViewCell: BaseTableViewCell {
    
    var logoView: UIImageView!
    var titleView: UILabel!
    var descView: UILabel!
    
    override func initUi() {
        ui = PLUi(view: contentView) { this, child in
            // 此处放置 Ui 代码
        }
    }
    
    func configure(withModel model: Site) {
        titleView.text = model.title
        descView.text = model.desc
    }
}

你可能感兴趣的:(PLUi - Swift 3 纯代码构造 UI 的函数式帮助类)