QuicklySwift
该代码只为提高 Swift UI 层的开发效率,使代码编写更简洁、更易读、更易维护。
示例
要运行示例项目,请先克隆存储库,然后在 Example 目录下运行 pod install
。
要求
安装
QuicklySwift 可通过 CocoaPods 获取。要安装它,只需将以下行添加到您的 Podfile 文件中
pod 'QuicklySwift'
如果无法下载最新版本,可以在 podfile 中使用
pod 'QuicklySwift', :git => 'https://github.com/rztime/QuicklySwift.git'
作者
rztime, [电子邮件地址被遮蔽], qq交流群:580839749
许可证
QuicklySwift在MIT许可证下可用。有关更多信息,请参阅LICENSE文件。
最近更新日志
相关 | 说明 |
---|---|
v0.3.0 | 新增通过url或path获取文件属性,新增部分功能 |
v0.2.0 | 优化部分KVO监听的事件,添加弹窗支持图文混排,添加字符串的安全(表情)截取方法,对一些方法进行优化,添加动态规划方法 |
v0.1.8 | 添加view飘灰配置功能、对数字进行小数位转换等等 |
v0.1.7 | 添加虚线、一些监听、属性改变回调、定时器的用法等等 |
v0.1.5 | 添加权限判断方法 QuicklyAuthorization.result(with: QAuthorizationType.photoLibrary) { result in } |
v0.1.4 | 添加push、pop的自定义转场动画方法和demo 添加预览相册的push、pop动画 |
v0.1.3 | 添加手势、气泡、气泡弹窗等等方法 |
说明
如何提高 UI 开发效率:
1. 写UI时,能快速布局,层级清晰
2. 代码写着顺手
3. 代码简洁、少
4. 代码功能封装完善
如何降低 UI 维护成本
1. 代码少、出错的少、修改的时候更容易
2. 代码简洁易读、view所属层级清晰、约束依赖清晰
3. view相关配置、使用更集中,要查找更改更轻松
总结
相关 | 说明 |
---|---|
QGrayFloatManager | 飘灰管理:为view在指定区域内添加一个黑白滤镜,并可一键控制滤镜开关 |
UIView | 常用属性的快速设置、扩展设置(高斯模糊、气泡、手势、圆角等等)、size改变之后的回调、deinit之后的回调、常用动画等等 |
UIScrollView | contentSize和contentOffset改变回调、delegate的回调等等 |
UITableView、UICollectionView | dataSource, delegate的回调等等 |
UITextView、UITextField | 快速使用的方法、长度限制、占位符、delegate回调等等 |
UILabel、UIButton、UIImage、UIColor | 相关的方法的快速使用 |
UIViewController | 生命周期的回调、push、pop的自定义跳转动画,相册预览 |
UIAlertController | 快速使用 |
QuicklyPopmenu | 菜单view |
Number, Int等等 | 根据配置转换成字符串,取整,保留几位小数,不足位补0等等 |
等等 |
- UIView
除了本身已有的属性,可通过添加q的方式使用,如
let view = UIView().qbackgroundColor(.red).qisUserInteractionEnabled(true)
另外添加的扩展方法, 所有继承UIView的,都可以使用这些方法
方法 | 说明 |
---|---|
qbody | 添加子视图 |
qmakeConstraints | 视图添加约束,在父视图body添加view的方法中使用 |
qsizeChanged | size发生改变的回调 |
qshowToWindow | 在window上显示隐藏的回调 |
qdeinit | 释放之后的回调 |
qcornerRadiusCustom | 设置四个角部分圆角 |
qgaussBlur | 设置高斯模糊 |
qgradientColors | 设置渐变色 |
qairbubble | 添加气泡背景 |
qtap | 单击手势回调 |
qtapNumberof | 单击手势 |
qpanNumberof | 拖拽手势 |
qdrag | 使view可以在父视图上随意拖拽,并可自动吸边 |
qlongpress | 长按手势 |
qswipe | 轻扫手势 |
qtoImage | 将view转换为图片 |
qshake | view抖动 |
qrotation | 旋转(转圈) |
qtransform | 平移、旋转一定角度、缩放 |
QuikclyPopmenu | 在指定位置弹出菜单view |
qgrayfloat | 页面添加飘灰区域 |
- UITextField、UITextView
除了本身的属性可以通过q方法调用, delegate的方法直接使用block即可回调使用
let textField = UITextField.init(frame: .zero)
.qplaceholder("输入文本")
.qmaxCount(10) // 最多输入字数 区别在于一个表情长度=1
// .qmaxLength(10) // 最多输入字数 区别在于一个表情长度=2(也有大于2)
.qbackgroundColor(.lightGray.withAlphaComponent(0.3))
.qleftView(UIView.init(frame: .init(x: 0, y: 0, width: 30, height: 30)), .always)
.qclearButtonMode(.always)
.qfont(.systemFont(ofSize: 16))
.qtextColor(.red)
.qtextChanged({ textField in
print("text 改变")
}).qshouldChangeText { textField, range, replaceText in
return true
}.qshouldBeginEditing { textField in
return true
}
方法 | 说明 |
---|---|
qmaxCount | 最多输入字符个数(表情算做1个) |
qmaxLength | 最多输入字符长度(表情的长度有可能是2、4、7) |
qplaceholder | textView设置空白站位 |
qattributedPlaceholder | 富文本站位 |
- UIControl: 一样的,一些属性和方法可以通过q来调用
方法 | 说明 |
---|---|
qactionFor | 使用block方式实现事件的回调 |
qisSelectedChanged | control isselected 设置改变之后的回调 |
qisEnabledChanged | control isEnabled 状态改变之后的回调 |
let btn = UIButton.init(type: .custom).qactionFor(.touchUpInside, handler: { sender in
print("按钮点击")
}).qisSelectedChanged { sender in
// isSelected 状态改变
sender.backgroundColor = sender.isSelected ? .red : .lightGray
}.qisEnabledChanged { sender in
// isEnabled 状态改变
sender.alpha = sender.isEnabled ? 1 : 0.3
}
- UIStackView
使用了QStackView,当stackView内的子视图全部隐藏时,stackView也隐藏,这样当stackView作为其他stackView的子视图时,也可以收回去
方法 | 说明 |
---|---|
VStackView.qbody() | 直接生成一个垂直排列的stackView |
HStackView.qbody() | 直接生成一个水平排列的stackView |
UIStackView.qbody() | 按需生成stackView |
- UIScrollView
除了本身的方法,滚动的delegate的所有方法可以通过block回调来使用时, 如:
let scrollView = UIScrollView()
.qdidScroll { scrollView in
}.qdidEndScroll { scrollView in
}.qdidZoom { scrollView in
}
另外添加了扩展
方法 | 说明 |
---|---|
qcontentSizeChanged | scrollView的contentSize改变回调 |
qcontentOffsetChanged | scrollView的contentOffset改变回调 |
qdidEndScroll | scrollView滚动停止的回调 |
- UITableView、UICollectionView:
除了继承UIScrollView,能使用ScrollView的q方法
他们本身也不需要在使用delegate、dataSource, 如下:(collectionView使用方法一样)
使用qnumberofRows、qcell、qdidSelectRow就可以完成tableView使用
tableView
.qregister(TestTableViewCell.self, identifier: "cell")
.qnumberofSections {
return 1
}.qnumberofRows { section in
return 100
}.qcell { tableView, indexPath in
let cell: TestTableViewCell = (tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TestTableViewCell) ?? .init(style: .default, reuseIdentifier: "cell")
let rad = indexPath.row
cell.nameLabel.text = "阿斯加德发按理阿斯加德发按理"
cell.timeLabel.text = "05:20"
cell.cover.isHidden = rad % 3 == 0
return cell
}.qdidSelectRow { tableView, indexPath in
tableView.deselectRow(at: indexPath, animated: false)
print("---- selected:\(indexPath)")
}
.qcanEdit { indexPath in
return true
}.qcanMove { indexPath in
return true
}.qeditActions { indexPath in
let aciton = UITableViewRowAction.init(style: .default, title: "删除") { _, indexPath in
}
return [aciton]
}
.qcontentSizeChanged { scrollView in
print("contentsize changed 改变")
}.qcontentOffsetChanged { scrollView in
print("contentoffset changed 改变")
}.qdidScroll { scrollView in
print("scrollView 滚动")
}.qdidEndScroll { scrollView in
print("scrollView 停止滚动")
}
- UIViewController
方法 | 说明 |
---|---|
qtopViewController | 获取当前显示的最顶层的vc |
qdidpop | vc被pop出去之后的回调 |
qwillAppear | vc将要显示的回调 |
qdidAppear | vc已经显示的回调 |
qwillDisAppear | vc将要消失的回调 |
qdidDisAppear | vc 已经消失的回调 |
qdeinit | vc被释放的回调 |
- QuicklyAppFrame.swift
app主框架qAppFrame
方法 | 说明 |
---|---|
qAppFrame.navigationController | 获取当前最顶层vc所在的navigationController |
qAppFrame.viewControllers | 获取当前最顶层vc所在的navigationController的所有vc |
qAppFrame.pushViewController() | 跳转vc,如果因为卡顿,导致重复跳转到这个vc,会过滤掉 |
qAppFrame.present() | 模态跳转 |
qAppFrame.popViewController() | pop |
qAppFrame.remove(vc) | 移除当前栈里的vc |
- QuicklyConstant.swift
这里有一些常量,都是动态获取,根据当前屏幕、app的状态来获取
方法 | 说明 |
---|---|
qappKeyWindow | APP的keywindow |
qappSafeAreaInsets | keywindow的上下左右安全区域 |
qisiPhoneNotch | 是否是刘海屏 |
qbottomSafeHeight | 底部安全区域(竖屏可能为34,横屏21) |
qstatusbarHeight | 状态栏高度,iOS13之后,高度不确定了 |
qnavigationbarHeight | 导航栏高度= 状态栏高度+44 |
qisdeviceLandscape | 当前设备是否是横屏 |
QuicklySwift,主要是对现有部分常用的控件添加了一些扩展方法,为了方便使用,所有方法名属性名添加“q”为前缀
不使用命名空间,是为了使用链式语法,使用时更方便快捷,让开发效率更高
一: 快速布局
主要扩展UIView、UIStackView,在添加子视图、以及对子视图添加约束时,代码更紧凑,层次清晰易懂。
UIStackView
public extension UIStackView {
/// 生成QStackView
@discardableResult
class func qbody(_ axis: NSLayoutConstraint.Axis, _ space: CGFloat, _ align: UIStackView.Alignment, _ distribution: UIStackView.Distribution, _ views: [UIView]?) -> QStackView {
let stackView = QStackView.init(frame: .zero)
stackView.qaxis(axis).qspacing(space).qalignment(align).qdistribution(distribution)
if let views = views, views.count > 0 {
stackView.qbody(views)
}
return stackView
}
}
/// 垂直UIStackView
public struct VStackView {
/// 垂直UIStackView
@discardableResult
static public func qbody(_ views: [UIView]) -> QStackView {
return QStackView.qbody(.vertical, 5, .fill, .equalSpacing, views)
}
}
/// 水平UIStackView
public struct HStackView {
/// 水平UIStackView
@discardableResult
static public func qbody(_ views: [UIView]) -> QStackView {
return QStackView.qbody(.horizontal, 5, .fill, .equalSpacing, views)
}
}
普通View
extentsion UIView {
/// 添加子视图views, 如果是stackView,会添加addArrangedSubview
func qbody(_ views: [UIView]) -> Self {}
/// 设置约束。 注意,这个方法仅仅是方便qaddSubviews() qbody() 方法里的view使用
/// 需要调用qactiveConstraints() 激活,
/// 方法里已经激活了,可以不需要调用
func qmakeConstraints(_ closure: ((_ make: ConstraintMaker) -> Void)?) -> Self {}
}
示例代码
class QuickLayoutViewController: UIViewController {
let label1 = UILabel().qtext("文本框1文本框1文本框1文本框1")
let label2 = UILabel().qtext("文本框2文本框2文本框2文本框2")
let label3 = UILabel().qtext("文本框3文本框3文本框3文本框3")
let btn1 = UIButton.init(type: .custom).qtitle("按钮1按钮1按钮1按钮1").qtitleColor(.black).qbackgroundColor(.lightGray)
let btn2 = UIButton.init(type: .custom).qtitle("按钮2按钮2按钮2按钮2").qtitleColor(.black).qbackgroundColor(.lightGray)
let btn3 = UIButton.init(type: .custom).qtitle("按钮3按钮3按钮3按钮3").qtitleColor(.black).qbackgroundColor(.lightGray)
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
self.view.qbody([
label1.qmakeConstraints({ make in
make.left.right.equalToSuperview().inset(20)
make.top.equalToSuperview().inset(qnavigationbarHeight)
}),
label2.qmakeConstraints({ make in
make.left.right.equalTo(self.label1)
make.top.equalTo(self.label1.snp.bottom).offset(30)
}),
label3.qmakeConstraints({ make in
make.left.right.equalTo(self.label1)
make.top.equalTo(self.label2.snp.bottom).offset(30)
}),
/// 垂直排列的UIStackView
VStackView.qbody([
btn1,
btn2,
btn3
]).qmakeConstraints({ make in
make.left.right.equalToSuperview()
make.top.equalTo(self.label3.snp.bottom).offset(50)
}).qspacing(15),
/// 也可以直接使用数组 :[UIView], qjoined()方法,将直接组合生成一个UIStackView
// [btn1, btn2, btn3].qjoined(aixs: .vertical, spacing: 15, align: .fill, distribution: .equalSpacing)
// .qmakeConstraints({ make in
// make.left.right.equalToSuperview()
// make.top.equalTo(self.label3.snp.bottom).offset(50)
// })
])
}
}
'view' 的 'body' 包含三个label、一个垂直 VstackView,在 qbody 添加数组时,使用 'qmakeConstraints' 直接设置约束
'view' 子视图层级、依赖更清晰
优点:比起以前(以前需要将子视图添加到父视图后才能设置约束),现在在数组中添加子视图、设置约束时,代码更紧凑,约束作为一种属性,会延迟到添加子视图后激活
/// 添加子视图, 如果是stackView,会添加addArrangedSubview
@discardableResult
func qbody(_ views: [UIView]) -> Self {
if let stackView = self as? UIStackView {
views.forEach({stackView.addArrangedSubview($0)})
} else {
views.forEach({self.addSubview($0)})
}
/// 激活约束
views.forEach({$0.qactiveConstraints()})
return self
}
/// 激活snap的约束
@discardableResult
func qactiveConstraints() -> Self {
if let constraintMake = qconstraintMake {
self.snp.makeConstraints(constraintMake)
self.qconstraintMake = nil
}
return self
}
关于页面飘灰
飘灰配置
/// 一键开关
QGrayFloatManager.shared.isgrayActive = true // or false
/// 飘灰视图层级,默认为1, view.layer.zPosition默认为0,数值越大,图层越处于顶层
QGrayFloatManager.shared.zposition = 1
view的飘灰有两种方式,约束和frame
/// view 整个飘灰
view.qgrayfloat(edges: .zero)
/// view 部分区域可以多个飘灰
view.qgrayfloat(tag: 1, frame: .init(x: 0, y: 0, width: 100, height: 100))
view.qgrayfloat(tag: 2, frame: .init(x: 0, y: 400, width: 100, height: 100))
/// 如果不想让view里的imageView飘灰
view.addSubview(imageView)
imageView.layer.zposition = 2 //
关于类似tableView飘灰前10行、scrollView飘灰首屏等,方法类似
/// tableView 只飘灰前10行
tableView.qcontentSizeChanged { [weak self] tb in
guard let tableView = self?.tableView, let self = self else { return }
/// 只飘灰前10列,后边的正常显示
var maxRow = min(10, self.rowCount - 1)
maxRow = max(0, maxRow)
let y = tableView.rectForRow(at: IndexPath.init(row: maxRow, section: 0)).maxY
tableView.qgrayfloat(frame: .init(x: 0, y: 0, width: qscreenwidth, height: y))
}
/// scrollView 首屏飘灰,之后正常显示
scrollView.qframeChanged { view in
view.qgrayfloat(frame: .init(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))
}
二: UITableView和UICollectionView的快速使用
不需要使用delegate和dataSource,全部使用q方法来使用,更简单
示例代码:UICollectionView的用法相同
tableView
.qregister(TestTableViewCell.self, identifier: "cell")
.qnumberofSections {
return 1
}.qnumberofRows { section in
return 100
}.qcell { tableView, indexPath in
let cell: TestTableViewCell = (tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TestTableViewCell) ?? .init(style: .default, reuseIdentifier: "cell")
let rad = indexPath.row
cell.nameLabel.text = "阿斯加德发按理阿斯加德发按理"
cell.timeLabel.text = "05:20"
cell.cover.isHidden = rad % 3 == 0
return cell
}.qdidSelectRow { tableView, indexPath in
tableView.deselectRow(at: indexPath, animated: false)
print("---- selected:\(indexPath)")
}
当然也有其他关于tableView的方法,比如编辑、移动、删除、willDisplayCell、EndDisplayCell等等
tableView
.qcanEdit { indexPath in
return true
}.qcanMove { indexPath in
return true
}.qeditActions { indexPath in
let aciton = UITableViewRowAction.init(style: .default, title: "删除") { _, indexPath in
}
return [aciton]
}
三: UIScrollView的快速使用
UIScrollView的相关的delegate回调,全部可以通过q方法来获取,并另外添加了content size改变、content offset改变、did End Scroll结束滚动的回调监听
示例代码:以下不需要设置delegate,直接使用,示例代码用tableView展示,collectionView等等继承scrollView的,都是同样的方法
tableView
.qcontentSizeChanged { scrollView in
print("contentsize changed 改变")
}.qcontentOffsetChanged { scrollView in
print("contentoffset changed 改变")
}.qdidScroll { scrollView in
print("scrollView 滚动")
}.qdidEndScroll { scrollView in
print("scrollView 停止滚动")
}
四: UIView的方法
这里举例部分代码,基本上所有的属性都有q方法快速使用
视图添加size改变之后的回调
添加了单击手势(如果是UIControl,有touchup响应事件,两者只用qtap)
添加了拖拽方法qdrag(style)(可自动吸边、或保持手势释放位置)
/// 拖拽时,style
public enum QDragPanStyle {
/// 普通模式,手势停止则view停止, 如果view太靠近边界,需要修正的位置edge
case normal(edge: UIEdgeInsets)
/// 吸边,停止之后,自动吸附到水平(左右)靠近的一边
case nearHorizontal(edge: UIEdgeInsets)
/// 吸边,停止之后,自动吸附到垂直(上下)靠近的一边
case nearVertical(edge: UIEdgeInsets)
/// 吸边,停止之后,自动吸附到靠的最近的一边
case nearBorder(edge: UIEdgeInsets)
}
添加了旋转、平移、缩放、抖动、循环旋转等等
添加了设置部分圆角
let testView = UIView()
.qsizeChanged { view in
print("view size 改变")
}.qtap { view in
print("view 单击手势 响应")
}.qdeinit {
// 被释放
}.qshowToWindow { view, show in
// 显示在window
}
.qdrag(style) // 拖拽 可自动吸边
///.qborder(.black, 1)
/// 设置左上、右下圆角
///.qcornerRadiuscustom([.topLeft, .bottomRight], radii: 50)
.qcornerRadius(50, false)
.qbackgroundColor(.lightGray)
.qshadow(.red, .init(width: 5, height: 5), radius: 20)
等等等等
let label = UILabel()
.qfont(.systemFont(ofSize: 15))
.qtextColor(.red)
.qnumberOfLines(0)
.qtap { view in
print("点击了label")
}
等等等等
let btn = UIButton.init(type: .custom)
.qtitle("按钮")
.qtitleColor(.red)
.qactionFor(.touchUpInside) { sender in
print("点击了按钮 事件响应")
}.qtap { view in
print("点击了按钮 tap响应 如果设置了tap, qactionFor touchUpInside方法无效 将被覆盖")
}.qisSelectedChanged { sender in
// isSelected 状态改变
sender.backgroundColor = sender.isSelected ? .red : .lightGray
}.qisEnabledChanged { sender in
// isEnabled 状态改变
sender.alpha = sender.isEnabled ? 1 : 0.3
}
qactionFor是所有继承自UControl都可以使用的
public extension UIControl {
/// control的事件回调
@discardableResult
func qactionFor(_ event: UIControl.Event, handler:((_ sender: UIControl) -> Void)?) -> Self { }
}
UITextField、UITextView的方法都类似
添加了最多输入字数限制
UITextView添加了占位字符串
let textField = UITextField.init(frame: .zero)
.qplaceholder("输入文本")
.qmaxCount(10) // 最多输入字数 区别在于一个表情长度=1
// .qmaxLength(10) // 最多输入字数 区别在于一个表情长度=2(也有大于2)
.qbackgroundColor(.lightGray.withAlphaComponent(0.3))
.qleftView(UIView.init(frame: .init(x: 0, y: 0, width: 30, height: 30)), .always)
.qclearButtonMode(.always)
.qfont(.systemFont(ofSize: 16))
.qtextColor(.red)
.qtextChanged({ textField in
print("text 改变")
}).qshouldChangeText { textField, range, replaceText in
return true
}.qshouldBeginEditing { textField in
return true
}
let textView = UITextView.init(frame: .zero)
.qplaceholder("请输入文本")
.qmaxCount(10) // 最多输入字数 区别在于一个表情长度=1
// .qmaxLength(10) // 最多输入字数 区别在于一个表情长度=2(也有大于2)
.qfont(.systemFont(ofSize: 17))
.qtextColor(.qhex(0xff0000))
.qbackgroundColor(.lightGray.withAlphaComponent(0.3))
.qtextChanged({ textView in
print("text 改变")
})
五: UIViewController的方法
给Controller添加了生命周期的回调
// let vc = UIViewController()
vc.qdidpop {
print("self 从navigationController 中 pop 出去了")
}.qdidAppear {
}.qdidDisAppear {
}.qwillAppear {
}.qwillDisAppear {
}.qdeinit {
}
UIAlertController
let actions = ["按钮1", "按钮2", "按钮3"]
UIAlertController.qwith(title: "标题", "提示内容", actions: actions, cancelTitle: "取消", style: .alert) { index in
print("点击了\(actions[index]), index是取自数组下标")
} cancel: {
print("点击了取消")
}
六: 一些常量
app 的 keywindow
/// app 的 keywindow
public var qappKeyWindow: UIWindow {
if let window = UIApplication.shared.delegate?.window, let window = window {
return window
}
if #available(iOS 13.0, *) {
let arraySet: Set = UIApplication.shared.connectedScenes
let windowScene: UIWindowScene? = arraySet.first(where: { sce in
if let _ = sce as? UIWindowScene {
return true
}
return false
}) as? UIWindowScene
if let window = windowScene?.windows.first(where: {$0.isKeyWindow}) {
return window
}
}
if let window = UIApplication.shared.windows.first(where: {$0.isKeyWindow}) {
return window
}
return UIApplication.shared.keyWindow ?? .init()
}
app 上下左右安全距离,无刘海屏时,顶部为状态栏高度,其余为0
/// app 上下左右安全距离,没有刘海屏,则顶部为状态栏高度,其余为0
public var qappSafeAreaInsets: UIEdgeInsets {
if #available(iOS 11.0, *) {
return qappKeyWindow.safeAreaInsets
} else {
return .init(top: qstatusbarHeight, left: 0, bottom: 0, right: 0)
}
}
是否是刘海屏
/// 是否是刘海屏
public var qisiPhoneNotch: Bool {
if #available(iOS 11.0, *) {
return qappKeyWindow.safeAreaInsets.bottom > 0
} else {
return false
}
}
底部安全区域,刘海屏为34,也有21(横屏时21),实时获取
/// 底部安全区域 刘海屏是34, 也有21
public var qbottomSafeHeight: CGFloat {
return qappSafeAreaInsets.bottom
}
/// 导航栏高度
/// iOS13之后,导航栏高度不固定
public var qstatusbarHeight: CGFloat {
if #available(iOS 13.0, *),
let height = qappKeyWindow.windowScene?.statusBarManager?.statusBarFrame.size.height {
return height
}
return UIApplication.shared.statusBarFrame.size.height
}
/// 导航栏高度 44 + 状态栏高度
public var qnavigationbarHeight: CGFloat {
return qstatusbarHeight + 44
}
/// 设备是否横屏
public var qisdeviceLandscape: Bool {
var type: UIInterfaceOrientation = UIApplication.shared.statusBarOrientation
if #available(iOS 13.0, *), let t = qappKeyWindow.windowScene?.interfaceOrientation {
type = t
}
switch type {
case .landscapeLeft, .landscapeRight:
return true
case.portrait, .portraitUpsideDown, .unknown:
return false
}
}
七、APP整体功能 “qAppFrame”
QuicklyAppFrame.swift
qAppFrame.navigationController? // 获取当前最顶层vc所在的navigationController
qAppFrame.viewControllers // 获取当前最顶层vc所在的navigationController 的所有的栈viewControllers
qAppFrame.pushViewController(vc, animated:) // 跳转到vc,忽略重复的跳转vc (如果因为卡顿或者其他原因,重复跳转到这一个vc时)
qAppFrame.present(vc, animated: complete) // 模态打开vc
/// 从当前navigationController栈里移除vc
qAppFrame.remove(viewController: UIViewController, animate: Bool)
qAppFrame.remove(viewControllerClass: AnyClass, animate: Bool)
题外一
最初只想写 qbody、qmakeConstraints 以及 stackView 的方法,只想把添加子视图以及约束整合起来,
因为在实际使用中,添加的子视图太多,以及加了自动布局约束之后,
越到后边要理清子视图之间的依赖以及所属的父视图和层级,就越麻烦
self.view.qbody([
label1.qmakeConstraints({ make in
make.left.right.equalToSuperview().inset(20)
make.top.equalToSuperview().inset(qnavigationbarHeight)
}),
label2.qmakeConstraints({ make in
make.left.right.equalTo(self.label1)
make.top.equalTo(self.label1.snp.bottom).offset(30)
}),
label3.qmakeConstraints({ make in
make.left.right.equalTo(self.label1)
make.top.equalTo(self.label2.snp.bottom).offset(30)
}),
/// 垂直排列的UIStackView
VStackView.qbody([
btn1,
btn2,
btn3
]).qmakeConstraints({ make in
make.left.right.equalToSuperview()
make.top.equalTo(self.label3.snp.bottom).offset(50)
}).qspacing(15),
/// 也可以直接使用数组 :[UIView], qjoined()方法,将直接组合生成一个UIStackView
// [btn1, btn2, btn3].qjoined(aixs: .vertical, spacing: 15, align: .fill, distribution: .equalSpacing)
// .qmakeConstraints({ make in
// make.left.right.equalToSuperview()
// make.top.equalTo(self.label3.snp.bottom).offset(50)
// })
])
而像这样之后,一眼就知道 label1、label2、label3 属于 self.view,以及清楚他们的约束情况
也知道 btn1、btn2、btn3 组成了一个垂直排列的 stackView,放在 self.view
这样在后期维护的时候,如果要调整 UI,就不用花太多时间去理清原本的 UI 逻辑
题外二
之后就想把 tableview cocllectionView scrollView 的一些 delegate dataSource 方法,通过 block 的方式能快速写出来
然后是一些动画、拖拽手势、额外的 size、contentSize、contentOffset、deinit、vc 的 pop、高斯模糊、view 的渐变色等等就慢慢也搞出来