-
Notifications
You must be signed in to change notification settings - Fork 368
/
JXSegmentedBaseDataSource.swift
177 lines (155 loc) · 8.58 KB
/
JXSegmentedBaseDataSource.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//
// JXSegmentedBaseDataSource.swift
// JXSegmentedView
//
// Created by jiaxin on 2018/12/28.
// Copyright © 2018 jiaxin. All rights reserved.
//
import Foundation
import UIKit
open class JXSegmentedBaseDataSource: JXSegmentedViewDataSource {
/// 最终传递给JXSegmentedView的数据源数组
open var dataSource = [JXSegmentedBaseItemModel]()
/// cell的宽度。为JXSegmentedViewAutomaticDimension时就以内容计算的宽度为准,否则以itemWidth的具体值为准。
open var itemWidth: CGFloat = JXSegmentedViewAutomaticDimension
/// 真实的item宽度 = itemWidth + itemWidthIncrement。
open var itemWidthIncrement: CGFloat = 0
/// item之前的间距
open var itemSpacing: CGFloat = 20
/// 当collectionView.contentSize.width小于JXSegmentedView的宽度时,是否将itemSpacing均分。
open var isItemSpacingAverageEnabled: Bool = true
/// item左右滚动过渡时,是否允许渐变。比如JXSegmentedTitleDataSource的titleZoom、titleNormalColor、titleStrokeWidth等渐变。
open var isItemTransitionEnabled: Bool = true
/// 选中的时候,是否需要动画过渡。自定义的cell需要自己处理动画过渡逻辑,动画处理逻辑参考`JXSegmentedTitleCell`
open var isSelectedAnimable: Bool = false
/// 选中动画的时长
open var selectedAnimationDuration: TimeInterval = 0.25
/// 是否允许item宽度缩放
open var isItemWidthZoomEnabled: Bool = false
/// 是否允许item宽度缩放动画
open var isItemWidthZoomAnimable: Bool = true
/// item宽度选中时的scale
open var itemWidthSelectedZoomScale: CGFloat = 1.5
@available(*, deprecated, renamed: "itemWidth")
open var itemContentWidth: CGFloat = JXSegmentedViewAutomaticDimension {
didSet {
itemWidth = itemContentWidth
}
}
private var animator: JXSegmentedAnimator?
deinit {
animator?.stop()
animator = nil
}
public init() {
}
/// 配置完各种属性之后,需要手动调用该方法,更新数据源
///
/// - Parameter selectedIndex: 当前选中的index
open func reloadData(selectedIndex: Int) {
animator?.stop()
animator = nil
dataSource.removeAll()
for index in 0..<preferredItemCount() {
let itemModel = preferredItemModelInstance()
preferredRefreshItemModel(itemModel, at: index, selectedIndex: selectedIndex)
dataSource.append(itemModel)
}
}
open func preferredItemCount() -> Int {
return 0
}
/// 子类需要重载该方法,用于返回自己定义的JXSegmentedBaseItemModel子类实例
open func preferredItemModelInstance() -> JXSegmentedBaseItemModel {
return JXSegmentedBaseItemModel()
}
/// 子类需要重载该方法,用于返回索引为index的item宽度
open func preferredSegmentedView(_ segmentedView: JXSegmentedView, widthForItemAt index: Int) -> CGFloat {
return itemWidthIncrement
}
/// 子类需要重载该方法,用于更新索引为index的itemModel
open func preferredRefreshItemModel(_ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int) {
itemModel.index = index
itemModel.isItemTransitionEnabled = isItemTransitionEnabled
itemModel.isSelectedAnimable = isSelectedAnimable
itemModel.selectedAnimationDuration = selectedAnimationDuration
itemModel.isItemWidthZoomEnabled = isItemWidthZoomEnabled
itemModel.itemWidthNormalZoomScale = 1
itemModel.itemWidthSelectedZoomScale = itemWidthSelectedZoomScale
if index == selectedIndex {
itemModel.isSelected = true
itemModel.itemWidthCurrentZoomScale = itemModel.itemWidthSelectedZoomScale
}else {
itemModel.isSelected = false
itemModel.itemWidthCurrentZoomScale = itemModel.itemWidthNormalZoomScale
}
}
//MARK: - JXSegmentedViewDataSource
open func itemDataSource(in segmentedView: JXSegmentedView) -> [JXSegmentedBaseItemModel] {
return dataSource
}
/// 自定义子类请继承方法`func preferredWidthForItem(at index: Int) -> CGFloat`
public final func segmentedView(_ segmentedView: JXSegmentedView, widthForItemAt index: Int) -> CGFloat {
return preferredSegmentedView(segmentedView, widthForItemAt: index)
}
public func segmentedView(_ segmentedView: JXSegmentedView, widthForItemContentAt index: Int) -> CGFloat {
return self.segmentedView(segmentedView, widthForItemAt: index)
}
open func registerCellClass(in segmentedView: JXSegmentedView) {
}
open func segmentedView(_ segmentedView: JXSegmentedView, cellForItemAt index: Int) -> JXSegmentedBaseCell {
return JXSegmentedBaseCell()
}
open func refreshItemModel(_ segmentedView: JXSegmentedView, currentSelectedItemModel: JXSegmentedBaseItemModel, willSelectedItemModel: JXSegmentedBaseItemModel, selectedType: JXSegmentedViewItemSelectedType) {
currentSelectedItemModel.isSelected = false
willSelectedItemModel.isSelected = true
if isItemWidthZoomEnabled {
if (selectedType == .scroll && !isItemTransitionEnabled) ||
selectedType == .click ||
selectedType == .code {
animator = JXSegmentedAnimator()
animator?.duration = selectedAnimationDuration
animator?.progressClosure = {[weak self] (percent) in
guard let self = self else { return }
currentSelectedItemModel.itemWidthCurrentZoomScale = JXSegmentedViewTool.interpolate(from: currentSelectedItemModel.itemWidthSelectedZoomScale, to: currentSelectedItemModel.itemWidthNormalZoomScale, percent: percent)
currentSelectedItemModel.itemWidth = self.itemWidthWithZoom(at: currentSelectedItemModel.index, model: currentSelectedItemModel)
willSelectedItemModel.itemWidthCurrentZoomScale = JXSegmentedViewTool.interpolate(from: willSelectedItemModel.itemWidthNormalZoomScale, to: willSelectedItemModel.itemWidthSelectedZoomScale, percent: percent)
willSelectedItemModel.itemWidth = self.itemWidthWithZoom(at: willSelectedItemModel.index, model: willSelectedItemModel)
segmentedView.collectionView.collectionViewLayout.invalidateLayout()
}
if isItemWidthZoomAnimable {
animator?.start()
}else {
animator?.stop()
}
}
}else {
currentSelectedItemModel.itemWidthCurrentZoomScale = currentSelectedItemModel.itemWidthNormalZoomScale
willSelectedItemModel.itemWidthCurrentZoomScale = willSelectedItemModel.itemWidthSelectedZoomScale
}
}
open func refreshItemModel(_ segmentedView: JXSegmentedView, leftItemModel: JXSegmentedBaseItemModel, rightItemModel: JXSegmentedBaseItemModel, percent: CGFloat) {
//如果正在进行itemWidth缩放动画,用户又立马滚动了contentScrollView,需要停止动画。
animator?.stop()
animator = nil
if isItemWidthZoomEnabled && isItemTransitionEnabled {
//允许itemWidth缩放动画且允许item渐变过渡
leftItemModel.itemWidthCurrentZoomScale = JXSegmentedViewTool.interpolate(from: leftItemModel.itemWidthSelectedZoomScale, to: leftItemModel.itemWidthNormalZoomScale, percent: percent)
leftItemModel.itemWidth = itemWidthWithZoom(at: leftItemModel.index, model: leftItemModel)
rightItemModel.itemWidthCurrentZoomScale = JXSegmentedViewTool.interpolate(from: rightItemModel.itemWidthNormalZoomScale, to: rightItemModel.itemWidthSelectedZoomScale, percent: percent)
rightItemModel.itemWidth = itemWidthWithZoom(at: rightItemModel.index, model: rightItemModel)
segmentedView.collectionView.collectionViewLayout.invalidateLayout()
}
}
/// 自定义子类请继承方法`func preferredRefreshItemModel(_ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int)`
public final func refreshItemModel(_ segmentedView: JXSegmentedView, _ itemModel: JXSegmentedBaseItemModel, at index: Int, selectedIndex: Int) {
preferredRefreshItemModel(itemModel, at: index, selectedIndex: selectedIndex)
}
private func itemWidthWithZoom(at index: Int, model: JXSegmentedBaseItemModel) -> CGFloat {
var width = self.segmentedView(JXSegmentedView(), widthForItemAt: index)
if isItemWidthZoomEnabled {
width *= model.itemWidthCurrentZoomScale
}
return width
}
}