Parade
简介
向 UICollectionView、UITableView 或 UIScrollViews 内的单元格通信,一直是一个挑战。它几乎总是依赖于一个混乱的过程,尝试将滚动进度传递到触发特殊滚动效果的单元格中。我们设计了这个框架,以最大限度地减少动画视图所需的努力。通过一个简单的基于块构建器,我们使定义视图状态变得简单——从它们出现的地方到它们消失的地方。
功能
- 支持 UICollectionView、UITableView、UIScrollview
- 简单基于块的语法
- 最小化集成需求
- 支持链式动画视图
- 可调整的进度范围
- 46 种不同的参数曲线
交互式演示
项目包含一个示例应用程序,其中包含了以下已实现的示例,用于下面的动画gif中的滚动效果。
包含示例 | |
---|---|
![]() |
演示包含一个根视图 ParallaxImageViewController ,并且以下顺序显示了这些单元格作为示例- ParallaxIntroCollectionViewCell : 缩放 / 变换 / 透明度- ParallaxScaleCollectionViewCell : 缩放 / 居中- ParallaxDoubleImageCollectionViewCell : 居中 / 变换- ParallaxImageAppearCollectionViewCell : 动画链式偏移- ParallaxImageCollectionViewCell : 缩放 / 透明度 / 变换- ParallaxTheEndCollectionViewCell : 3D 变换注意:示例中也包含将在文档后面讨论的自定义范围。 |
安装
通信
- 如果您发现了 错误,或有 特性请求,请提交一个 issue。
- 如果您想 贡献,请查阅 贡献指南 并提交一个 pull request。
- 如果您 贡献,请确保代码覆盖率有 100%
基本用法
从核心来看,这个框架为您的可动画视图定义了 起始 和 结束 状态,滚动视图将在这些状态之间进行插值。起始状态定义了视图在滚动进入屏幕时的出现位置——而结束状态定义了视图消失的位置。
除了定义状态之外,还需要实现 PDAnimatableType 在要动画的视图中,其他的都是无缝的。每个动画由开发者定义的 起始 或 结束 状态组成——再加上首次视图开始滚动时自动配置的 快照 状态。
在进行创建之前,请参考下面的图来了解进度计算的坐标空间。进度是相对于scrollview自身的边界而言的。在水平滚动时,X值差异到视口中心点的值等于scrollview自身的宽度。正如垂直滚动时Y值的变化,如以下所示。
注意:可以对这些范围进行调整,具体说明可在下方的界限进度范围部分找到。
初始化
在应用程序启动时通过 application(:didFinishLaunchingWithOptions:)
方法初始化Parade。一旦初始化完成,基本的UIScrollView将开始将滚动进度传递给其内部包含的动画视图。
UIScrollView.initializeParade()
创建可动画视图
第一步,通过实现 PDAnimatableType
协议使得视图可动画化,scrollview则会开始向其子视图传递进度。
public protocol PDAnimatableType {
// The progress animator definition that
// interpolates over animatable properties
func configuredAnimator() -> PDAnimator;
}
以下任一视图及其子视图都可以实现 PDAnimatableType
并进行动画处理:
UICollectionViewCell
UITableViewCell
UIScrollView
的子视图
然而,在scrollview可以传递进度之前,需要定义一个与scrollview将要跟踪的相对滚动方向相匹配的动画器。框架附带基于递归块构建器的工具,它可以简化创建复杂的动画,以便在滚动中进行插值。
垂直方向滚动动画器
以下是一个创建垂直滚动动画器的示例。该闭包返回一个动画器,可以用它为层次结构中的任何特定视图创建状态。
func configuredAnimator() -> PDAnimator {
/* Create a vertical tracking animator call this class method */
return PDAnimator.newVerticalAnimator { (animator) in
}
}
水平方向滚动动画器
以下是一个创建水平动画器的示例,该动画器可以水平滚动。闭包返回一个可以使用它来创建和指定视图任何特定状态的动画器。
func configuredAnimator() -> PDAnimator {
/* Create a horizontal tracking animator with this class method */
return PDAnimator.newHorizontalAnimator { (animator) in
}
}
闭包返回一个可以根据需要为特定视图创建和到状态的动画器。
配置动画状态
配置结束状态
为了使一个视图相对于滚动进度淡入,可以通过如下方式调用startState(for:)
方法定义一个起始状态。每次添加状态时,都会返回一个状态制造器,可以用来遍历多个属性。
func configuredAnimator() -> PDAnimator {
let offScreenAlpha : CGFloat = 0.0
return PDAnimator.newVerticalAnimator { (animator) in
animator.startState(for: animatedImageView, { (s) in
s.alpha(offScreenAlpha)
})
}
}
配置结束状态
在上一个例子基础上,为了使一个视图相对于滚动进度隐去,通过调用endState(for:)
方法定义一个结束状态。
func configuredAnimator() -> PDAnimator {
let offScreenAlpha : CGFloat = 0.0
return PDAnimator.newVerticalAnimator { (animator) in
animator.startState(for: animatedImageView, { (s) in
s.alpha(offScreenAlpha)
}).endState(for: animatedImageView, { (s) in
s.alpha(offScreenAlpha)
})
}
}
配置起始和结束状态
在上一个例子基础上,出现的透明度消失似乎是相同的。如果起始和结束状态值相同,可以通过使用startEndState(for:)
方法来同时设置两个状态。
func configuredAnimator() -> PDAnimator {
let offScreenAlpha : CGFloat = 0.0
return PDAnimator.newVerticalAnimator { (animator) in
animator.startEndState(for: animatedImageView, { (s) in
s.alpha(offScreenAlpha)
})
}
}
为多个视图设置起始和结束状态
如果需要多个视图执行相同的动画,已经定义了辅助方法来为视图数组创建动画。以下是如何使用它们的示例。
func configuredAnimator() -> PDAnimator {
var animatedViews = [view1, view2, view3, view4]
let offScreenAlpha : CGFloat = 0.0
return PDAnimator.newVerticalAnimator { (animator) in
animator.startEndState(forViews: animatedViews, { (s) in
s.alpha(offScreenAlpha)
})
}
}
这也可以用于多个视图。
func startState(forViews views: [UIView], _ callback : ... ) -> PDAnimationMaker
func endState(forViews views: [UIView], _ callback : ... ) -> PDAnimationMaker
func startEndState(forViews views: [UIView], _ callback : ... ) -> PDAnimationMaker
状态属性
该框架提供了许多辅助方法,可以轻松地定义视图及其支持层的状态属性。
/* View Property Setters */
public func alpha(_ value : CGFloat) -> PDAnimatablePropertyMaker
public func backgroundColor(_ value : UIColor) -> PDAnimatablePropertyMaker
public func bounds(_ value : CGSize) -> PDAnimatablePropertyMaker
public func center(_ value : CGPoint) -> PDAnimatablePropertyMaker
public func size(_ value : CGSize) -> PDAnimatablePropertyMaker
public func transform(_ value : CGAffineTransform) -> PDAnimatablePropertyMaker
/* Layer Property Setters */
public func borderColor(_ value : UIColor) -> PDAnimatablePropertyMaker
public func borderWidth(_ value : CGFloat) -> PDAnimatablePropertyMaker
public func contentsRect(_ value : CGRect) -> PDAnimatablePropertyMaker
public func cornerRadius(_ value : CGFloat) -> PDAnimatablePropertyMaker
public func shadowColor(_ value : UIColor) -> PDAnimatablePropertyMaker
public func shadowOffset(_ value : CGSize) -> PDAnimatablePropertyMaker
public func shadowOpacity(_ value : CGFloat) -> PDAnimatablePropertyMaker
public func shadowRadius(_ value : CGFloat) -> PDAnimatablePropertyMaker
public func transform3D(_ value : CATransform3D) -> PDAnimatablePropertyMaker
public func zPosition(_ value : CGFloat) -> PDAnimatablePropertyMaker
如果没有定义setter,并且需要将特定属性设置为插值之间,还定义了两个setter,适用于视图及其支持层并使用KVC。
public func viewValue(_ value : Any?, forKey key : String) -> PDAnimatablePropertyMaker
public func layerValue(_ value : Any?, forKey key : String) -> PDAnimatablePropertyMaker
参数式缓动
有46种不同的参数曲线可以应用于每个属性的独特插值。框架内置了以下支持的参数曲线,可以单独应用于每个属性。
.inSine .inOutSine .outSine .outInSine |
.inQuadratic .inOutQuadratic .outQuadratic .outInQuadratic |
.inCubic .inOutCubic .outCubic .outInCubic |
.inQuartic .inOutQuartic .outQuartic .outInQuartic |
.inQuintic .inOutQuintic .outQuintic .outInQuintic |
.inAtan .inOutAtan .outAtan * |
.inExponential .inOutExponential .outExponential .outInExponential |
.inCircular .inOutCircular .outCircular .outInCircular |
.inBack .inOutBack .outBack .outInBack |
.inElastic .inOutElastic .outElastic .outInElastic |
.inBounce .inOutBounce .outBounce .outInBounce |
.linear .smoothStep .smootherStep * |
在构建视图状态时附加到状态的属性定义中。有关支持的某些参数曲线的参考信息,请访问此处
func configuredAnimator() -> PDAnimator {
let offScreenAlpha : CGFloat = 0.0
let offScreenTransform = CGAffineTransform.identity.scaledBy(x: 0.5, y: 0.5)
return PDAnimator.newVerticalAnimator { (animator) in
animator.startEndState(for: animatedImageView, { (s) in
s.alpha(offScreenAlpha).easing.(.inSine)
s.transform(offScreenTransform).easing(.inOutCubic)
})
}
}
高级视图动画
链式动画
动画进度不必仅适用于正在滚动的顶级视图。如果一个子视图实现了 PDAnimatableType
,并且是子视图层次结构的一部分,将其附加到动画器可以创建一个链。
然后动画器将进度与子视图的动画器进行通信,子视图可以附加到子视图,或者子视图甚至可以将它们附加到子视图,以创建最终可以遍历多个级别以产生一些有趣效果的链。要附加一个动画视图,请按照如下方式调用 attachAnimatableView(:)
方法。
func configuredAnimator() -> PDAnimator {
let offScreenAlpha : CGFloat = 0.0
let offScreenTransform = CGAffineTransform.identity.translatedBy(x: 0.0, y: 100.0)
return PDAnimator.newVerticalAnimator { (animator) in
animator.startEndState(for: animatedImageView, { (s) in
s.transform(offScreenTransform).easing(.inOutCubic)
}).attachAnimatableView(animatedImageView)
}
}
边界进度范围
有时需要调整进度实际发生的位置。当动画视图的大小小于滚动视图的边界大小时,这特别有用。观察定义的范围示例以及它们如何影响进度。通过定义一个范围,这基本上是在告诉滚动视图从0到100的进度计数从哪里开始。
可以像对缓动函数那样,按属性定义不同的范围,从而允许不同的属性在不同的坐标空间内插值。以下示例定义了每个属性的两个不同范围,并且可以在上面直观地参考哪里将发生插值。
func configuredAnimator() -> PDAnimator {
let offScreenAlpha : CGFloat = 0.0
let offScreenTransform = CGAffineTransform.identity.translatedBy(x: 0.0, y: 100.0)
return PDAnimator.newVerticalAnimator { (animator) in
animator.startState(for: animatedImageView, { (s) in
s.transform(offScreenTransform).easing(.inOutCubic).range(0.5...1.0)
s.alpha(offScreenTransform).easing(.inOutCubic).range(0.25...0.75)
})
}
}
许可
Parade 在 MIT 许可下发布。有关详细信息,请参阅 许可。