StackUI
像 SwiftUI 一样使用 UIStackView
。
使用 @propertyWrapper
、@resultBuilder
、链式语法和其他在 SwiftUI 中使用的功能,使 UIStackView
更易于使用。对于类如 Label
、Button
和 ImageView
,仍然按照 UIKit 的方式使用,但使用链式语法进行了封装。
由于 SwiftUI 需要iOS13+ 系统,因此在实际项目中几乎不可能使用 SwiftUI。但 SwiftUI 的功能确实非常吸引人,因此 StackUI 是在 UIStackView
范围内实现 SwiftUI 的。您可以在 StackUI 中体验一些 SwiftUI 的功能,这样您就可以更早地学习新功能。
如果您厌倦了配置 UIKit 的约束,那么 StackUI 将是您的得力助手。声明式布局使世界更美好。
功能
- 类似于 SwiftUI 的声明式语法;
- 数据驱动的 UI,数据更新后 UI 自动更新;
- 支持
HScrollStack
、VScrollStack
,当内容超出堆叠的宽度和高度时,滚动将自动启用; - 链式语法配置 UIKit;
- 可以灵活扩展自定义类以支持
StackUI
。
需求
- iOS 9.0+
- XCode 13.0+
- Swift 5.4+
安装
CocoaPods
target'<Your Target Name>' do
pod'StackUI'
//If you want to use RxSwift
pod'StackUI/RxSwift'
end
先执行 pod repo update
,然后执行 pod install
SPM
- 文件> Swift Packages> 添加包依赖
- 添加 https://github.com/CalvinChina/StackUI.git
- 选择 "Up to Next Major" 并设置为 "0.0.5"
- 如果需要使用
RxSwift
特性,请选择StackUISwithRxSwift
产品
示例
HStack
HStack(alignment: .center, spacing: 5) {
ImageView().image(UIImage(named: "avatar")).size(width: 40, height: 40)
Label("user nickname").font(.systemFont(ofSize: 18, weight: .medium))
}
VStack
VStack {
Label("user nickname").font(.systemFont(ofSize: 18, weight: .medium))
Label().text("The user is lazy, nothing left!").font(.systemFont(ofSize: 12)).textColor(.gray)
}
HScrollStack
和 ForIn
var users = [UserModel]()
for index in 0...25 {
let model = UserModel(avatar: "avatar", name: "user\(index)", desc: "user description\(index)")
users.append(model)
}
HScrollStack() {
for model in users {
HStack(alignment: .center, spacing: 5) {
Spacer(spacing: 12)
ImageView().image(UIImage(named: model.avatar)).size(width: 80, height: 80)
VStack {
Label(model.name).font(.systemFont(ofSize: 18, weight: .medium))
Label().text(model.desc).font(.systemFont(ofSize: 12)).textColor(.gray)
}
Divider()
}.size(width: 300)
}
}
@Live
在属性前添加 @Live
,使用时加上 $
符号前缀。更新属性时,关联的 UIKit 将自动更新界面。
//definition
@Live var nickName: String? = "User nickname"
@Live var desc: String? = "The user is lazy, and nothing is left!"
//use
HStack(distribution: .equalCentering, alignment: .center) {
HStack(alignment: .center, spacing: 5) {
ImageView().image(UIImage(named: "avatar")).size(width: 40, height: 40)
VStack {
Label($nickName).font(.systemFont(ofSize: 18, weight: .medium))
Label().text($desc).font(.systemFont(ofSize: 12)).textColor(.gray)
}
}
ImageView().image(UIImage(named: "arrow_right"))
}
//update
nickName = "Jay Chou"
desc = "Ouch, not bad ❤️"
RxSwift
的 Driver
//definition
var nickName = PublishSubject<String?>()
var desc = PublishSubject<String?>()
//use
HStack(distribution: .equalCentering, alignment: .center) {
HStack(alignment: .center, spacing: 5) {
ImageView().image(UIImage(named: "avatar")).size(width: 40, height: 40)
VStack {
Label(nickName.asDriver(onErrorJustReturn: nil)).font(.systemFont(ofSize: 18, weight: .medium))
Label().text(desc.asDriver(onErrorJustReturn: nil)).font(.systemFont(ofSize: 12)).textColor(.gray)
}
}
ImageView().image(UIImage(named: "arrow_right"))
}
//update
nickName.onNext("Jay Chou")
desc.onNext("Ouch, not bad ❤️")
StackUI
自定义视图支持 您可以自定义父类
从 View
、Label
等继承,然后扩展自定义属性。
class CustomView: View {
var customColor: UIColor = .black
func customColor(_ customColor: UIColor) -> Self {
self.customColor = customColor
self.backgroundColor = customColor
return self
}
}
class CustomLabel: Label {
var customColor: UIColor = .black
func customColor(_ customColor: UIColor) -> Self {
self.customColor = customColor
self.textColor = customColor
return self
}
}
条件语句
var isShowInfo: Bool = false
HStack(distribution: .equalCentering, alignment: .center) {
HStack(alignment: .center, spacing: 5) {
ImageView().image(UIImage(named: "avatar")).size(width: 40, height: 40)
if self.isShowInfo == true {
VStack {
Label("User nickname").font(.systemFont(ofSize: 18, weight: .medium))
Label().text("The user is lazy, nothing left!").font(.systemFont(ofSize: 12)).textColor(.gray)
}
}else {
Label("No Information")
}
}
ImageView().image(UIImage(named: "arrow_right"))
}
父类不能修改,必须遵循协议
遵循如 StackUIView
和 StackUILabel
的协议,然后扩展自定义属性。
class CustomViewFromProtocol: UIView, StackUIView {
var customColor: UIColor = .black
func customColor(_ customColor: UIColor) -> Self {
self.customColor = customColor
self.backgroundColor = customColor
return self
}
}
class CustomLabelFromProtocol: UILabel, StackUILabel {
var customColor: UIColor = .black
func customColor(_ customColor: UIColor) -> Self {
self.customColor = customColor
self.textColor = customColor
return self
}
}
尚不支持链式语法进行属性配置
通过apply closure进行统一配置
HStack(alignment: .center, spacing: 5) {
Label().text(model.desc).apply {label in
//If there are attributes that are not defined by the chain syntax, they can be configured in the apply closure, or they can be submitted to Issue for support.
label.font = UIFont.systemFont(ofSize: 10)
label.textColor = .gray
}
}
ViewBox
使用
有时视图需要边距。此时,您可以先将视图放入ViewBox中,然后再设置内边距,这样可以间接设置视图的上、下、左、右边距。如下代码所示:标签的上、左、下、右边距分别为top: 10, left: 20, bottom: 10, right: 20
。
ViewBox(paddings: .init(top: 10, left: 20, bottom: 10, right: 20)) {
Label("Hobbies: writing code;").font(. systemFont(ofSize: 15)).numberOfLines(0)
}.backgroundColor(.lightGray)
当前支持的类
Layer
的相关属性在View
类中进行配置View
ImageView
Control
Label
Button
TextField
TextView
Stepper
SwitchUI
PageControl
Slider
Spacer
Divider
ActivityIndicatorView
ScrollView
TableView
CollectionView
如果有其他需要支持的类和属性,请提交Issue或Pull Request。