帮助类 - 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
中的:
添加到 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
就可以改成以下的写法,显得更加精炼简洁
(ViewController
从 UIViewController
的继承需要改成 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
}
}