ViewController中的頁面布局
目前的頁面布局的常規(guī)方式
通常的布局的代碼會(huì)寫在viewDidLoad
方法中, 那么在整個(gè)viewcontroller
的生命周期中只會(huì)被調(diào)用一次, 那么如果頁面中需要一些簡單的動(dòng)畫, 就需要去更新頁面的autoLayout
代碼.
頁面布局代碼的優(yōu)化方案
在viewController
的生命周期有一些方法是可以被重復(fù)調(diào)用的, 比如說viewDidLayoutSubviews
, 而且該方法可以通過self.view.setNeedsLayout
調(diào)用到, 所以我們可以在這個(gè)方法里面去實(shí)現(xiàn)頁面的布局代碼, 以減少viewController
中viewDidLoad
方法中過多的頁面布局代碼.
Swift的具體的實(shí)現(xiàn)
因?yàn)?strong>swift的protocol
可以定義需要實(shí)現(xiàn)的var
, 可以通過將該頁面需要的subviews
先在protocol
中定義好, 然后可以通過protocol
中的associatedtype
和類似codable
這種組合協(xié)議的方式將viewcontroller
和頁面元素的protocol
組合, 并且實(shí)現(xiàn)doLayoutWithController
方法. 以下為protocol
的方法和默認(rèn)實(shí)現(xiàn)
ControllerViewElements
基本協(xié)議, 使用時(shí)需要其他協(xié)議繼承該協(xié)議
protocol ControllerViewElements where Self: UIViewController {
var isNeedLayout: Bool {get set}
func controllerViewShouldLayout() -> Bool
func controllerViewDidLayout(_ isLayout: inout Bool)
func controllerViewNeedRelayout(_ isNeed: inout Bool)
}
extension ControllerViewElements where Self: UIViewController{
func controllerViewShouldLayout() -> Bool {
if self.isNeedLayout { return false }
return true
}
func controllerViewDidLayout(_ isNeedLayout: inout Bool) {
var isNeedLayout = isNeedLayout
DispatchQueue.main.async {
if isNeedLayout {
fatalError("the controller alread layouted")
} else {
isNeedLayout = true
}
}
}
func controllerViewNeedRelayout(_ isNeedRelayout: inout Bool) {
var isNeedRelayout = isNeedRelayout
DispatchQueue.main.async {
if isNeedRelayout {
isNeedRelayout = false
} else {
fatalError("no need call this method")
}
}
}
}
ControllerLayoutProtocol
在ViewController
的viewDidLayoutSubviews
中調(diào)用doLayoutWithController
protocol ControllerLayoutProtocol {
associatedtype LayoutTarget
func doLayoutWithController(_ parent: LayoutTarget)
}
ViewControllerLayouParameter
設(shè)置該頁面元素autoLayout
時(shí)的具體參數(shù)
struct ControllerLayouParameter {
let top_view_top: CGFloat = 20
let top_view_left: CGFloat = 20
let top_view_right: CGFloat = 20
let top_view_height: CGFloat = 45
let mid_view_top: CGFloat = 20
let mid_view_left: CGFloat = 20
let mid_view_right: CGFloat = 20
let mid_view_default_height: CGFloat = 45
let mid_view_changed_height: CGFloat = 0
let mid_view_animated_duartion = 0.5
}
ViewControllerLayoutElements
定義該頁面需要的頁面元素
protocl ViewControllerLayoutElements: ControllerViewElements {
var topView: UIView! {get set}
var midView: UIView! {get set}
}
ViewControllerLayout
執(zhí)行頁面元素的autoLayout
代碼
struct ViewControllerLayout: ControllerLayoutProtocol {
typealias LayoutTarget = ViewControllerLayoutElements
func doLayoutWithController(_ parent: LayoutTarget) {
let params = ControllerLayouParameter()
parent.topView.snp.makeConstraints { (make) in
make.top.equalTo(params.top_view_top)
make.left.equalTo(params.top_view.left)
make.right.equalTo(params.top_view.right)
make.height.equalTo(params.top_view_height)
}
parent.midView.snp.updateConstraints { (make) in
make.height.equalTo(params.mid_view_default_height)
}
parent.midView.snp.makeConstraints { (make) in
make.top.equalTo(params.mid_view_top)
make.left.equalTo(params.mid_view.left)
make.right.equalTo(params.mid_view.right)
}
}
func updateMidViewHeight(_ parent: LayoutTarget, isUpdate: Bool) {
let params = ControllerLayoutParamater()
let changedHeight = params.mid_view_changed_height
let defaultHeight = params.mid_view_default_height
let height = isUpdate ? changedHeight : defaultHeight
parent.midView.snp.updateConstraints { (make) in
make.height.equalTo(height)
}
let duration = params.mid_view_animated_duartion
UIView.animate(withDuration: duration) {
parent.view.layoutIfNeeded()
}
}
}
ViewController
中使用該方式布局的代碼
final class ViewController: UIViewController, ViewControllerLayoutElements {
var isNeedLayout: Bool = false
var isUpdateMid: Bool = false {
didSet {
if isUpdateMid != oldValue {
self.view.setNeedsLayout()
}
}
}
lazy var topView: UIView! = {
let internalTopView = UIView()
//TODO: set params for topView
return internalTopView
}()
lazy var topView: UIView! = {
let internalTopView = UIView()
//TODO: set params for midView
return internalTopView
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let layout = ViewControllerLayout()
if self.controllerViewShouldLayout() {
layout.doLayoutWithController(self)
self.controllerViewDidLayout(self.isNeedLayout)
}
layout.updateMidViewHeight(self, isUpdate: self.isUpdateMid)
}
//該方法需要button中調(diào)用, 需要去實(shí)現(xiàn)changedButton的布局和Button的屬性設(shè)置
@IBAction private func toChangedMid(_ sender: UIButton) {
self.isNeedLayout = true
}
//該方法需要button中調(diào)用, 需要去實(shí)現(xiàn)defaultButton的布局和Button的屬性設(shè)置
@IBAction private func toDefaultMid(_ sender: UIButton) {
self.isNeedLayout = false
}
}