PVView
PVView
是一个小型库,它可以帮助你制作出令人惊叹的视差视图。
安装
CocoaPods
使用编辑 Podfile
并指定依赖项
pod 'PVView', '~> 1.0.4'
要求
- iOS 9.0+
- Swift 5
概念
PVItemType
代表一个视差项PVActionType
代表项的动作PVView
是分页的- 每个页面包含一组
PVItemType
,而PVView
只更新当前页面上项的动作 - 在一个页面上,每个
PVItemType
都有一组PVActionType
- 所有
PVActionType
都通过 进度 更新。页面中的 进度 始终在 [0...1] 范围内 - 一个动作可能有
startOffset
和stopOffset
。这些参数描述了动作开始和结束的页面偏移量。(0 <=startOffset
<stopOffset
<= 1)
如何使用
1. 在合适的位置导入 PVView
。
import PVView
2. 通过实现 PVItemType
协议创建视差项。
enum YourItem: String, PVItemType {
case item1
case item2
var identifier: String {
return self.rawValue
}
}
3. 设置 PVView
的代理并实现 PVViewDelegate
协议
必须的代理方法
PVViewDelegate
有 4 个必须实现的方法
- 通过实现来指定
PVView
中的页面数量
func numberOfPages(in parallaxView: PVView) -> Int {
return 2
}
- 通过实现来指定页面上的项
func parallaxView(_ parallaxView: PVView, itemsOnPage pageIndex: Int) -> [PVItemType] {
return [YourItem.item1, .item2]
}
- 然后
PVView
会通过以下方式询问您要附加到项的视图
func parallaxview(_ parallaxView: PVView, viewForItem item: PVItemType) -> UIView
注意:此方法返回的视图将被缓存在项中,并且当缓存中没有此项的视图时才会调用此方法
- 最后,通过实现来指定将在项上运行的动作
func parallaxView(_ parallaxView: PVView, actionsOfItem item: PVItemType, onPage pageIndex: Int) -> [PVActionType] {
if pageIndex == 0 {
return [PVActionMove(fromOrigin: PVPoint(x: 50, y: 200), toOrigin: PVPoint(x: 250, y: 200))]
}
return []
}
注意:在这些方法中,您可以使用
PVView
的currentPageIndex
属性来在切换前获取页面的索引。
可选的代理方法
PVView
支持水平和垂直滚动。通过实现以下方法来指定PVView
的滚动方向(默认为水平)
func direction(of parallaxView: PVView) -> PVView.PVDirection
- 默认情况下,
PVItemType
的视图将自动添加为PVView
的子视图。您可以通过实现此方法来指定PVItemType
视图的容器视图(返回nil
表示容器视图为PVView
)
func parallaxView(_ parallaxView: PVView, containerViewForItem item: PVItemType, onPage pageIndex: Int) -> UIView?
- 在过渡到新页面之前,
PVView
将调用以下方法,这是您在新页面出现之前做什么的机会(例如,在上一项上添加一些动画或隐藏/显示一些视图...)
func parallaxView(_ parallaxView: PVView, willBeginTransitionTo pageIndex: Int)
- 在完成新页面的过渡后,
PVView
将调用以下方法,这是您在新页面出现后做什么的机会(例如,为项的动作设置初始状态...)
func parallaxView(_ parallaxView: PVView, didEndTransitionFrom previousPageIndex: Int?)
- 最后一个代理方法是
func parallaxView(_ parallaxView: PVView, didUpdate pageProgress: Double, onPage pageIndex: Int)
此方法在滚动过程中持续调用,以报告页面内的进度
4. 启动 PVView
parallaxView.reload()
更多
动作参数
动作的参数由 PVParameters
提供
startOffset
:动作开始的页面偏移量stopOffset
:动作结束的页面偏移量timingFunction
:一个根据页面进度计算动作进度的对象
示例
startOffset
= 0.2, stopOffset
= 0.6. 动作将在进度 = 0.2 时开始,在进度 = 0.6 时停止
时序函数
存在一个名为 PVTimingFunction
的内置函数。它与 CAMediaTimingFunction
相同,但提供了 evaluate
方法来计算动作的进度
您可以通过 PVTimingFunctionName
中定义的名称来初始化一个 PVTimingFunction
有关这些函数名称的详细信息,请参阅 缓动函数
动作组
PVActionGroup
表示一组动作。组内动作的参数是组的参数。
由于 PVActionGroup
是 PVActionBasicType
,因此可以通过调用组的 reverse
方法轻松反转组中的所有动作
反转动作序列
有时您想在一个序列中反转所有动作。存在一个内置方法可以帮助
public extension Sequence where Element: PVActionBasicType {
func reversedActions(with newParameters: PVParameters = .default) -> [Element] {
return self.map { $0.reverse(with: newParameters) }
}
}
相对点 & 大小
您可以使用 PVPoint
来创建利用相对点的操作,绝对点将基于容器视图的大小进行计算
PVActionMove(fromPosition: PVPoint(x: 2, y: 0.2, isRelative: true)
您可以使用 PVSize
创建利用相对大小的操作,绝对大小将基于容器视图的大小进行计算
PVActionSize(from: PVSize(width: 0.2, height: 0.2, isRelative: true),
to: PVSize(width: 0.4, height: 0.4, isRelative: true))
自定义
自定义操作
您可以通过实现 PVActionType
来轻松地自定义任何您想要的操作
public protocol PVActionType {
func update(_ progress: Double, target: UIView)
}
或者实现继承自 PVActionType
的 PVActionBasicType
public protocol PVActionBasicType: PVActionType {
var parameters: PVParameters { get }
func step(_ progress: Double, target: UIView)
func reverse(with newParameters: PVParameters) -> Self
}
示例:我编写了一个自定义操作名为 LetterSpacingAction
,这个操作将改变 UILabel
中字符的间距
struct LetterSpacingAction: PVActionBasicType {
let parameters: PVParameters
let fromSpacing: Double
let toSpacing: Double
let maxWidth: CGFloat
init(fromSpacing: Double, toSpacing: Double, maxWidth: CGFloat, parameters: PVParameters = .default) {
self.fromSpacing = fromSpacing
self.toSpacing = toSpacing
self.maxWidth = maxWidth
self.parameters = parameters
}
func step(_ progress: Double, target: UIView) {
guard let label = target as? UILabel else { return }
let current = fromSpacing + (toSpacing - fromSpacing) * progress
label.attributedText = NSAttributedString(string: label.text ?? "", attributes: [.kern : current])
label.frame = CGRect(origin: label.frame.origin, size: label.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude)))
}
func reverse(with newParameters: PVParameters) -> LetterSpacingAction {
return LetterSpacingAction(fromSpacing: toSpacing,
toSpacing: fromSpacing,
maxWidth: maxWidth,
parameters: newParameters)
}
}
自定义时间函数
您可以通过实现 PVTimingFunctionType
来创建任何时间函数
public protocol PVTimingFunctionType {
func evaluate(_ input: Double) -> Double
}
示例:创建一个 EaseOutSine 函数
struct EaseOutSineFunction: PVTimingFunctionType {
func evaluate(_ input: Double) -> Double {
return sin(input * Double.pi / 2)
}
}
文档
即将推出...
贡献力量
这是一个开源项目,我迫不及待地想看到你们许多精彩的想法。
许可协议
此代码根据MIT许可协议的条款和条件分发。