VATextureKit
Texture 库的包装器,添加了一些功能。
安装
CocoaPods
将以下内容添加到 Podfile 中
pod 'VATextureKitRx' // includes additional wrappers.
or
pod 'VATextureKit' // includes Texture nodes wrappers.
or
pod 'VATextureKitSpec' // includes only Layout Spec wrappers.
在终端中的项目目录中
pod install
或者只需尝试示例项目
pod try 'VATextureKit'
最低部署目标:iOS 11
布局规范
以下 LayoutSpec
DSL 组件可以用于组合简单或非常复杂的布局。
VATextureKit | Texture |
---|---|
列 | ASStackLayoutSpec(垂直) |
行 | ASStackLayoutSpec(水平) |
堆叠 | |
安全区域 | ASInsetLayoutSpec(带有安全区域内边距) |
.填充 | ASInsetLayoutSpec |
.包裹 | ASWrapperLayoutSpec |
.角 | ASCornerLayoutSpec |
.安全 | ASInsetLayoutSpec(带有安全区域内边距) |
.居中 | ASCenterLayoutSpec |
.比例 | ASRatioLayoutSpec |
.覆盖 | ASOverlayLayoutSpec |
.背景 | ASBackgroundLayoutSpec |
.相对 | ASRelativeLayoutSpec |
.绝对 | ASAbsoluteLayoutSpec |
列
使用 ASStackLayoutSpec
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
ASStackLayoutSpec(
direction: .vertical,
spacing: 8,
justifyContent: .start,
alignItems: .start,
children: [
firstRectangleNode,
secondRectangleNode,
]
)
}
使用 Column
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
Column(spacing: 8) {
firstRectangleNode
secondRectangleNode
}
}
示例
行
使用 ASStackLayoutSpec
ASStackLayoutSpec(
direction: .horizontal,
spacing: 4,
justifyContent: .spaceBetween,
alignItems: .start,
children: [
firstRectangleNode,
secondRectangleNode,
]
)
使用 Row
Row(spacing: 4, main: .spaceBetween) {
firstRectangleNode
secondRectangleNode
}
示例
安全区域
使用 ASStackLayoutSpec
在 ASDisplayNode
中,当 automaticallyRelayoutOnSafeAreaChanges = true
ASInsetLayoutSpec(
insets: UIEdgeInsets(
top: safeAreaInsets.top,
left: safeAreaInsets.left,
bottom: safeAreaInsets.bottom,
right: safeAreaInsets.right
),
child: ...
)
使用 SafeArea
SafeArea {
...
}
.填充
使用 ASInsetLayoutSpec
ASInsetLayoutSpec(
insets: UIEdgeInsets(
top: 8,
left: 8,
bottom: 8,
right: 8
),
child: titleTextNode
)
使用 .padding
titleTextNode
.padding(.all(8))
.包裹
使用 ASWrapperLayoutSpec
ASWrapperLayoutSpec(layoutElement: imageNode)
使用 .wrapped
imageNode
.wrapped()
.角
使用 ASWrapperLayoutSpec
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
let spec = ASCornerLayoutSpec(
child: imageNode,
corner: badgeNode,
location: .topRight
)
spec.offset = CGPoint(x: 4, y: 2)
spec.wrapsCorner = false
return spec
}
使用 .corner
imageNode
.corner(badgeNode, offset: CGPoint(x: 4, y: 2))
.安全
使用 ASStackLayoutSpec
在 ASDisplayNode
中,当 automaticallyRelayoutOnSafeAreaChanges = true
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
ASInsetLayoutSpec(
insets: UIEdgeInsets(
top: safeAreaInsets.top,
left: safeAreaInsets.left,
bottom: safeAreaInsets.bottom,
right: safeAreaInsets.right
),
child: listNode
)
}
使用 .safe
listNode
.safe(in: self)
.居中
使用 ASCenterLayoutSpec
ASCenterLayoutSpec(
centeringOptions: .XY,
sizingOptions: .minimumXY,
child: buttonNode
)
使用 .centered
buttonNode
.centered()
.比例
使用 ASRatioLayoutSpec
ASRatioLayoutSpec(
ratio: 2 / 3,
child: imageNode
)
使用 .ratio
imageNode
.ratio(2 / 3)
.覆盖
使用 ASOverlayLayoutSpec
ASOverlayLayoutSpec(
child: imageNode,
overlay: gradientNode
)
使用 .overlay
imageNode
.overlay(gradientNode)
.背景
使用 ASOverlayLayoutSpec
ASBackgroundLayoutSpec(
child: gradientNode,
background: imageNode
)
使用 .background
imageNode
.background(gradientNode)
.相对
使用 ASOverlayLayoutSpec
ASRelativeLayoutSpec(
horizontalPosition: .start,
verticalPosition: .end,
sizingOption: .minimumSize,
child: buttonNode
)
使用 .relatively
buttonNode
.relatively(horizontal: .start, vertical: .end)
.绝对
使用 ASAbsoluteLayoutSpec
buttonNode.style.preferredSize = frame.size
buttonNode.style.layoutPosition = frame.origin
return ASAbsoluteLayoutSpec(
sizing: .sizeToFit,
children: [buttonNode]
)
使用 .absolutely
buttonNode
.absolutely(frame: .frame, sizing: .sizeToFit)
更复杂的布局示例
使用 VATextureKit
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
Column(cross: .stretch) {
Row(main: .spaceBetween) {
Row(spacing: 8, cross: .center) {
testNameTextNode
testInfoButtonNode
}
testStatusTextNode
}
titleTextNode
.padding(.top(8))
resultTextNode
.padding(.top(32))
.centered(.X)
resultUnitsTextNode
.centered(.X)
referenceResultBarNode
.padding(.vertical(24))
Row(spacing: 16, cross: .center) {
Column(spacing: 8) {
Row(spacing: 8) {
resultBadgeImageNode
resultDescriptionTextNode
}
referenceValuesTextNode
}
accessoryImageNode
}
}
.padding(.all(16))
}
使用原始 Texture
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
ASInsetLayoutSpec(
insets: UIEdgeInsets(
top: 16,
left: 16,
bottom: 16,
right: 16
),
child: ASStackLayoutSpec(
direction: .vertical,
spacing: 0,
justifyContent: .start,
alignItems: .stretch,
children: [
ASStackLayoutSpec(
direction: .horizontal,
spacing: 0,
justifyContent: .spaceBetween,
alignItems: .start,
children: [
ASStackLayoutSpec(
direction: .horizontal,
spacing: 8,
justifyContent: .start,
alignItems: .center,
children: [
testNameTextNode,
testInfoButtonNode,
]
),
testStatusTextNode,
]
),
ASInsetLayoutSpec(
insets: UIEdgeInsets(
top: 8,
left: 0,
bottom: 0,
right: 0
),
child: titleTextNode
),
ASCenterLayoutSpec(
centeringOptions: .X,
sizingOptions: .minimumXY,
child: ASInsetLayoutSpec(
insets: UIEdgeInsets(
top: 32,
left: 0,
bottom: 0,
right: 0
),
child: resultTextNode
)
),
ASCenterLayoutSpec(
centeringOptions: .X,
sizingOptions: .minimumXY,
child: resultUnitsTextNode
),
ASInsetLayoutSpec(
insets: UIEdgeInsets(
top: 24,
left: 0,
bottom: 24,
right: 0
),
child: referenceResultBarNode
),
ASStackLayoutSpec(
direction: .horizontal,
spacing: 0,
justifyContent: .start,
alignItems: .center,
children: [
ASStackLayoutSpec(
direction: .vertical,
spacing: 8,
justifyContent: .start,
alignItems: .start,
children: [
ASStackLayoutSpec(
direction: .horizontal,
spacing: 8,
justifyContent: .start,
alignItems: .start,
children: [
resultBadgeImageNode,
resultDescriptionTextNode,
]
),
referenceValuesTextNode,
]
),
accessoryImageNode,
]
),
]
)
)
}
修饰符
.sized
设置 Node
的大小。
使用 style
imageNode.style.width = ASDimension(unit: .points, value: 320)
imageNode.style.height = ASDimension(unit: .points, value: 480)
使用 .sized
imageNode
.sized(width: 320, height: 480)
.flex
设置 Node
的 flex。
使用 style
titleTextNode.style.flexShrink = 0.1
titleTextNode.style.flexGrow = 1
使用 .flex
titleTextNode
.flex(shrink: 0.1, grow: 1)
.maxConstrained
设置 Node
可用最大大小。
使用 style
titleTextNode.style.maxWidth = ASDimension(unit: .points, value: 320)
titleTextNode.style.maxHeight = ASDimension(unit: .points, value: 100)
使用 .maxConstrained
titleTextNode
.maxConstrained(width: 320, height: 480)
.minConstrained
设置 Node
可用最小大小。
使用 style
titleTextNode.style.minWidth = ASDimension(unit: .points, value: 100)
titleTextNode.style.minHeight = ASDimension(unit: .points, value: 50)
使用 .minConstrained
titleTextNode
.minConstrained(width: 100, height: 50)
节点
VADisplayNode
ASDisplayNode
的子类,自动管理子节点和处理主题更新。
VATextNode
ASTextNode
的子类,处理内容大小和主题更新。具有默认文本样式。
VAButtonNode
ASButtonNode
的子类,带有 onTap
闭包。
VACellNode
ASCellNode
的子类,自动管理子节点并处理主题更新。
VAImageNode
ASImageNode
的子类,具有参数化初始化器。
VASpacerNode
一个 ASDisplayNode
子类,用于填充 Row
或 Column
中的空间。
VASafeAreaDisplayNode
一个自动在安全区更改时重新布局的 VADisplayNode
子类。
VABaseGradientNode
一个带有 CAGradientLayer
根层的 ASDisplayNode
子类。
VALinearGradientNode
一个使用参数化初始化器来简化线性渐变创建的 VABaseGradientNode
子类。
VARadialGradientNode
一个使用参数化初始化器来简化径向渐变创建的 VABaseGradientNode
子类。
VAShapeNode
一个带有 CAShapeLayer
根层的 ASDisplayNode
子类。
VAEmitterNode
一个带有 CAEmitterLayer
支持的 ASDisplayNode
子类。
VAReadMoreTextNode
一个具有“阅读更多”截断的简便方式的 VATextNode
子类。
代码
lazy var readMoreTextNode = VAReadMoreTextNode(
text: .loremText,
maximumNumberOfLines: 2,
readMore: .init(
text: "Read more",
fontStyle: .headline,
colorGetter: { $0.systemBlue }
)
)
示例
VACountingTextNode
一个带有计数初始化的 VATextNode
子类。
代码
countingTextNode.updateCount(to: Int.random(in: 0...1000))
示例
容器
VATableListNode
*是 VATextureKitRx
的一部分
一个用于声明式使用的 ASTableNode
子类。
VAViewController
处理主题更新的 ASDKViewController
的子类。
VANavigationController
处理主题更新和内容大小变化的 ASDKNavigationController
的子类。
VATabBarController
处理主题更新的 ASTabBarController
的子类。
VAWindow
处理主题更新和内容大小变化的 VAWindow
的子类。提供应用程序上下文。
VAContainerCellNode
用于将任何节点包装为 Cell Node。
包装器
VAViewWrapperNode
用于使用节点与 UIView
的容器。
VANodeWrapperView
用于使用视图与节点的容器。
VASizedViewWrapperNode
用于使用节点和继承其大小的 UIView
的容器。
动画
布局过渡动画
以简单的方式实现布局过渡动画。只需编写
override func animateLayoutTransition(_ context: ASContextTransitioning) {
animateLayoutTransition(context: context)
}
示例
节点动画
以简单的方式实现节点动画。
示例
pulseNode.animate(.scale(values: [1, 1.1, 0.9, 1.2, 0.8, 1.1, 0.9, 1]), duration: 1)
结果
更多示例
主题
支持简单的方式应用主题。默认浅色/深色或自定义初始化。
扩展
ASDimension
初始化支持。
使用原始 Texture
style.height = ASDimension(unit: .auto, value: 0)
style.height = ASDimension(unit: .points, value: height)
style.height = ASDimension(unit: .fraction, value: 0.3)
使用 VATextureKit
style.height = .auto
style.height = .points(height)
style.height = .fraction(0.3)
style.height = .fraction(percent: 30)
CGSize
数学
CGSize(width: 2, height: 2) * 2 = CGSize(width: 4, height: 4)
CGSize(width: 2, height: 2) + 1 = CGSize(width: 3, height: 3)
初始化器
CGSize(same: 16) == CGSize(width: 16, height: 16)
UIEdgeInsets
变量
/// (top, left)
origin: CGPoint
/// top + bottom
vertical: CGFloat
/// left + right
horizontal: CGFloat
初始化器
UIEdgeInsets(all: 16) == UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
UIEdgeInsets(vertical: 16) == UIEdgeInsets(top: 16, left: 0, bottom: 16, right: 0)
UIEdgeInsets(horizontal: 16) == UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
UIEdgeInsets(vertical: 4, horizontal: 8) == UIEdgeInsets(top: 4, left: 8, bottom: 4, right: 8)
预览
使用支持函数以简单方式实现节点预览。
sRepresentation(layout:)
属性包装器
*是 VATextureKitRx
的一部分
- 注:
- Relay(value:) (BehaviorRelay)
- Relay() (PublishRelay)
使用这些包装器可以使代码更加简洁。
BehaviorRelay
var someObs: Observable<String> { someRelay.asObservable() }
private let someRelay = BehaviorRelay<String>(value: "value")
...
someRelay.accept("value1")
变为
@Obs.Relay(value: "value")
var someObs: Observable<String>
...
_someObs.rx.accept("value1")
PublishRelay
var someObs: Observable<String> { someRelay.asObservable() }
private let someRelay = PublishRelay<String>()
变为
@Obs.Relay()
var someObs: Observable<String>