VATextureKit 2.1.0

VATextureKit 2.1.0

VAndrJ 维护。



 
依赖
Texture~> 3.2.0
VATextureKitSpec= 2.1.0
 

  • Volodymyr Andriienko

VATextureKit

Texture 库的包装器,增加了一些特性。

安装

CocoaPods

将以下内容添加到您的 Podfile 中:

pod 'VATextureKit'

在终端中的项目目录下:

pod install

或者只是试试示例项目

pod try 'VATextureKit'

布局规范

以下 LayoutSpec DSL 组件可用于组合简单或非常复杂的布局。

VATextureKit Texture
ASStackLayoutSpec(垂直)
ASStackLayoutSpec(水平)
堆叠
安全区域 ASInsetLayoutSpec(与安全区域边缘)
内边距 ASInsetLayoutSpec
.包裹 ASWrapperLayoutSpec
.角落 ASCornetLayoutSpec
.安全 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
    }
}

示例

Column example

ASStackLayoutSpec 一起使用

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
    ASStackLayoutSpec(
        direction: .horizontal,
        spacing: 4,
        justifyContent: .spaceBetween,
        alignItems: .start,
        children: [
            firstRectangleNode,
            secondRectangleNode,
        ]
    )
}

Row 一起使用

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
    Row(spacing: 4, main: .spaceBetween) {
        firstRectangleNode
        secondRectangleNode
    }
}

示例

Column example

堆叠

堆叠:

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
    Stack {
        firstRectangleNode
        secondRectangleNode
    }
}

示例

Column example

安全区域

ASDisplayNode 中,automaticallyRelayoutOnSafeAreaChanges = true 的情况下的 ASStackLayoutSpec

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
    ASInsetLayoutSpec(
        insets: UIEdgeInsets(
            top: safeAreaInsets.top,
            left: safeAreaInsets.left,
            bottom: safeAreaInsets.bottom,
            right: safeAreaInsets.right
        ),
        child: ...
    )
}

SafeArea 一起使用

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
    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))
.安全

ASDisplayNode 中,automaticallyRelayoutOnSafeAreaChanges = true 的情况下的 ASStackLayoutSpec

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
    ASInsetLayoutSpec(
        insets: UIEdgeInsets(
            top: safeAreaInsets.top,
            left: safeAreaInsets.left,
            bottom: safeAreaInsets.bottom,
            right: safeAreaInsets.right
        ),
        child: listNode
    )
}

.safe 一起使用

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
    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)
更复杂的布局示例

Cell layout

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

带有 onTap 闭包的 ASButtonNode 的一个子类。

VACellNode

ASCellNode 的一个子类,自动管理子节点并处理主题更新。

VAImageNode

具有参数化初始化器的 ASImageNode 的一个子类。

VASpacerNode

一个 ASDisplayNode 的子类,用于填充 Row / Column 中的空间。

VASafeAreaDisplayNode

一个子类,在安全区域变化时自动重排布局。

VABaseGradientNode

具有 CAGradientLayer 根层的 ASDisplayNode 的一个子类。

VALinearGradientNode

具有参数化初始化器的 VABaseGradientNode 的一个子类,简化线性渐变创建。

VARadialGradientNode

具有参数化初始化器的 VABaseGradientNode 的一个子类,简化径向渐变创建。

VATypingTextNode

具有打字动画的 VATextNode 的一个子类。

示例

Typing example

VAReadMoreTextNode

使用简便方式实现“阅读更多”截断的 VATextNode 的一个子类。

代码

lazy var readMoreTextNode = VAReadMoreTextNode(
    text: .loremText,
    maximumNumberOfLines: 2,
    readMore: .init(
        text: "Read more",
        fontStyle: .headline,
        colorGetter: { $0.systemBlue }
    )
)

示例

Read more example

VAShimmerNode

具有闪烁动画的 VADisplayNode 的一个子类。

示例

Shimmer example

容器

VAListNode

一个用于声明式的 ASCollectionNode 的子类。

示例

List example

Dynamic heights layout example

Spec layout example

VATableListNode

一个用于声明式的 ASTableNode 的子类。

VAPagerNode

一个用于声明式的 ASPagerNode 的子类。一些弯曲功能以模拟循环滚动。

示例

Pager node example

VAViewController

一个处理主题更新的 ASDKViewController 的子类。

VANavigationController

一个处理主题更新和内容大小变化的 ASDKNavigationController 的子类。

VATabBarController

一个处理主题更新的 ASTabBarController 的子类。

VAWindow

一个处理主题更新和内容大小变化的 VAWindow 的子类。提供应用上下文。

VAContainerCellNode

用于包装任何节点与 Cell 节点。

包装器

VAViewWrapperNode

用于使用节点包装 UIView 的容器。

VANodeWrapperView

用于使用视图包装节点的容器。

VASizedViewWrapperNode

用于使用节点和继承其大小的 UIView 的容器。

动画

布局过渡动画

以简单的方式实现布局过渡动画。只需编写

override func animateLayoutTransition(_ context: ASContextTransitioning) {
    animateLayoutTransition(context: context)
}

示例

Layout transition example

节点动画

以简单的方式实现节点动画。

示例

pulseNode.animate(.scale(values: [1, 1.1, 0.9, 1.2, 0.8, 1.1, 0.9, 1]), duration: 1)

结果

Layout transition example

更多示例

Layout transition example

主题

以简单的方式支持主题。默认支持浅色/深色或自定义初始化。

属性包装器

  • Obs
    • Relay(value:)(行为Relay)
    • Relay()(发布Relay)

使用这些包装器,代码变得更简洁。

行为Relay
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")
发布Relay
var someObs: Observable<String> { someRelay.asObservable() }

private let someRelay = PublishRelay<String>()

变为

@Obs.Relay()
var someObs: Observable<String>

扩展

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:)

Preview example

实验

VACountingTextNode

Link text node

VASlidingTabBarNode

Sliding tab bar

VALinkTextNode

Link text node