-
Notifications
You must be signed in to change notification settings - Fork 421
Expand file tree
/
Copy pathJXPagingListContainerView.swift
More file actions
607 lines (567 loc) · 27.6 KB
/
JXPagingListContainerView.swift
File metadata and controls
607 lines (567 loc) · 27.6 KB
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
//
// JXPagingListContainerView.swift
// JXSegmentedView
//
// Created by jiaxin on 2018/12/26.
// Copyright © 2018 jiaxin. All rights reserved.
//
import UIKit
/// 列表容器视图的类型
///- ScrollView: UIScrollView。优势:没有其他副作用。劣势:实时的视图内存占用相对大一点,因为所有加载之后的列表视图都在视图层级里面。
/// - CollectionView: 使用UICollectionView。优势:因为列表被添加到cell上,实时的视图内存占用更少,适合内存要求特别高的场景。劣势:因为cell重用机制的问题,导致列表被移除屏幕外之后,会被放入缓存区,而不存在于视图层级中。如果刚好你的列表使用了下拉刷新视图,在快速切换过程中,就会导致下拉刷新回调不成功的问题。一句话概括:使用CollectionView的时候,就不要让列表使用下拉刷新加载。
public enum JXPagingListContainerType {
case scrollView
case collectionView
}
@objc
public protocol JXPagingViewListViewDelegate {
/// 如果列表是VC,就返回VC.view
/// 如果列表是View,就返回View自己
///
/// - Returns: 返回列表视图
func listView() -> UIView
/// 返回listView内部持有的UIScrollView或UITableView或UICollectionView
/// 主要用于mainTableView已经显示了header,listView的contentOffset需要重置时,内部需要访问到外部传入进来的listView内的scrollView
///
/// - Returns: listView内部持有的UIScrollView或UITableView或UICollectionView
func listScrollView() -> UIScrollView
/// 当listView内部持有的UIScrollView或UITableView或UICollectionView的代理方法`scrollViewDidScroll`回调时,需要调用该代理方法传入的callback
///
/// - Parameter callback: `scrollViewDidScroll`回调时调用的callback
func listViewDidScrollCallback(callback: @escaping (UIScrollView)->())
/// 将要重置listScrollView的contentOffset
@objc optional func listScrollViewWillResetContentOffset()
/// 可选实现,列表将要显示的时候调用
///
/// - Parameter isSystemTrigger: 是否是由生命周期方法触发
@objc optional func listWillAppear(isSystemTrigger: Bool)
/// 可选实现,列表显示的时候调用
///
/// - Parameter isSystemTrigger: 是否是由生命周期方法触发
@objc optional func listDidAppear(isSystemTrigger: Bool)
/// 可选实现,列表将要消失的时候调用
///
/// - Parameter isSystemTrigger: 是否是由生命周期方法触发
@objc optional func listWillDisappear(isSystemTrigger: Bool)
/// 可选实现,列表消失的时候调用
///
/// - Parameter isSystemTrigger: 是否是由生命周期方法触发
@objc optional func listDidDisappear(isSystemTrigger: Bool)
}
@objc
public protocol JXPagingListContainerViewDataSource {
/// 返回list的数量
///
/// - Parameter listContainerView: JXPagingListContainerView
func numberOfLists(in listContainerView: JXPagingListContainerView) -> Int
/// 根据index初始化一个对应列表实例,需要是遵从`JXPagingViewListViewDelegate`协议的对象。
/// 如果列表是用自定义UIView封装的,就让自定义UIView遵从`JXPagingViewListViewDelegate`协议,该方法返回自定义UIView即可。
/// 如果列表是用自定义UIViewController封装的,就让自定义UIViewController遵从`JXPagingViewListViewDelegate`协议,该方法返回自定义UIViewController即可。
/// 注意:一定要是新生成的实例!!!
///
/// - Parameters:
/// - listContainerView: JXPagingListContainerView
/// - index: 目标index
/// - Returns: 遵从JXPagingViewListViewDelegate协议的实例
func listContainerView(_ listContainerView: JXPagingListContainerView, initListAt index: Int) -> JXPagingViewListViewDelegate
/// 控制能否初始化对应index的列表。有些业务需求,需要在某些情况才允许初始化某些列表,通过通过该代理实现控制。
@objc optional func listContainerView(_ listContainerView: JXPagingListContainerView, canInitListAt index: Int) -> Bool
/// 返回自定义UIScrollView或UICollectionView的Class
/// 某些特殊情况需要自己处理UIScrollView内部逻辑。比如项目用了FDFullscreenPopGesture,需要处理手势相关代理。
///
/// - Parameter listContainerView: JXPagingListContainerView
/// - Returns: 自定义UIScrollView实例
@objc optional func scrollViewClass(in listContainerView: JXPagingListContainerView) -> AnyClass
}
@objc protocol JXPagingListContainerViewDelegate {
@objc optional func listContainerViewDidScroll(_ listContainerView: JXPagingListContainerView)
@objc optional func listContainerViewWillBeginDragging(_ listContainerView: JXPagingListContainerView)
@objc optional func listContainerViewDidEndScrolling(_ listContainerView: JXPagingListContainerView)
@objc optional func listContainerView(_ listContainerView: JXPagingListContainerView, listDidAppearAt index: Int)
}
open class JXPagingListContainerView: UIView {
public private(set) var type: JXPagingListContainerType
public private(set) weak var dataSource: JXPagingListContainerViewDataSource?
public private(set) var scrollView: UIScrollView!
public var isCategoryNestPagingEnabled = false {
didSet {
if let containerScrollView = scrollView as? JXPagingListContainerScrollView {
containerScrollView.isCategoryNestPagingEnabled = isCategoryNestPagingEnabled
}else if let containerScrollView = scrollView as? JXPagingListContainerCollectionView {
containerScrollView.isCategoryNestPagingEnabled = isCategoryNestPagingEnabled
}
}
}
/// 已经加载过的列表字典。key是index,value是对应的列表
open var validListDict = [Int:JXPagingViewListViewDelegate]()
/// 滚动切换的时候,滚动距离超过一页的多少百分比,就触发列表的初始化。默认0.01(即列表显示了一点就触发加载)。范围0~1,开区间不包括0和1
open var initListPercent: CGFloat = 0.01 {
didSet {
if initListPercent <= 0 || initListPercent >= 1 {
assertionFailure("initListPercent值范围为开区间(0,1),即不包括0和1")
}
}
}
public var listCellBackgroundColor: UIColor = .white
/// 需要和segmentedView.defaultSelectedIndex保持一致,用于触发默认index列表的加载
public var defaultSelectedIndex: Int = 0 {
didSet {
currentIndex = defaultSelectedIndex
}
}
weak var delegate: JXPagingListContainerViewDelegate?
private var currentIndex: Int = 0
private var collectionView: UICollectionView!
private var containerVC: JXPagingListContainerViewController!
private var willAppearIndex: Int = -1
private var willDisappearIndex: Int = -1
public init(dataSource: JXPagingListContainerViewDataSource, type: JXPagingListContainerType = .collectionView) {
self.dataSource = dataSource
self.type = type
super.init(frame: CGRect.zero)
commonInit()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
open func commonInit() {
guard let dataSource = dataSource else { return }
containerVC = JXPagingListContainerViewController()
containerVC.view.backgroundColor = .clear
addSubview(containerVC.view)
containerVC.viewWillAppearClosure = {[weak self] in
self?.listWillAppear(at: self?.currentIndex ?? 0, isSystemTrigger: true)
}
containerVC.viewDidAppearClosure = {[weak self] in
self?.listDidAppear(at: self?.currentIndex ?? 0, isSystemTrigger: true)
}
containerVC.viewWillDisappearClosure = {[weak self] in
self?.listWillDisappear(at: self?.currentIndex ?? 0, isSystemTrigger: true)
}
containerVC.viewDidDisappearClosure = {[weak self] in
self?.listDidDisappear(at: self?.currentIndex ?? 0, isSystemTrigger: true)
}
if type == .scrollView {
if let scrollViewClass = dataSource.scrollViewClass?(in: self) as? UIScrollView.Type {
scrollView = scrollViewClass.init()
}else {
scrollView = JXPagingListContainerScrollView.init()
}
scrollView.backgroundColor = .clear
scrollView.delegate = self
scrollView.isPagingEnabled = true
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.scrollsToTop = false
scrollView.bounces = false
if #available(iOS 11.0, *) {
scrollView.contentInsetAdjustmentBehavior = .never
}
containerVC.view.addSubview(scrollView)
}else if type == .collectionView {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
if let collectionViewClass = dataSource.scrollViewClass?(in: self) as? UICollectionView.Type {
collectionView = collectionViewClass.init(frame: CGRect.zero, collectionViewLayout: layout)
}else {
collectionView = JXPagingListContainerCollectionView.init(frame: CGRect.zero, collectionViewLayout: layout)
}
collectionView.backgroundColor = .clear
collectionView.isPagingEnabled = true
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
collectionView.scrollsToTop = false
collectionView.bounces = false
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
if #available(iOS 10.0, *) {
collectionView.isPrefetchingEnabled = false
}
if #available(iOS 11.0, *) {
self.collectionView.contentInsetAdjustmentBehavior = .never
}
containerVC.view.addSubview(collectionView)
//让外部统一访问scrollView
scrollView = collectionView
}
}
open override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
var next: UIResponder? = newSuperview
while next != nil {
if let vc = next as? UIViewController{
vc.addChild(containerVC)
break
}
next = next?.next
}
}
open override func layoutSubviews() {
super.layoutSubviews()
guard let dataSource = dataSource else { return }
containerVC.view.frame = bounds
if type == .scrollView {
if scrollView.frame == CGRect.zero || scrollView.bounds.size != bounds.size {
scrollView.frame = bounds
scrollView.contentSize = CGSize(width: scrollView.bounds.size.width*CGFloat(dataSource.numberOfLists(in: self)), height: scrollView.bounds.size.height)
for (index, list) in validListDict {
list.listView().frame = CGRect(x: CGFloat(index)*scrollView.bounds.size.width, y: 0, width: scrollView.bounds.size.width, height: scrollView.bounds.size.height)
}
scrollView.contentOffset = CGPoint(x: CGFloat(currentIndex)*scrollView.bounds.size.width, y: 0)
}else {
scrollView.frame = bounds
scrollView.contentSize = CGSize(width: scrollView.bounds.size.width*CGFloat(dataSource.numberOfLists(in: self)), height: scrollView.bounds.size.height)
}
}else {
if collectionView.frame == CGRect.zero || collectionView.bounds.size != bounds.size {
collectionView.frame = bounds
collectionView.collectionViewLayout.invalidateLayout()
collectionView.reloadData()
collectionView.setContentOffset(CGPoint(x: CGFloat(currentIndex)*collectionView.bounds.size.width, y: 0), animated: false)
}else {
collectionView.frame = bounds
}
}
}
//MARK: - JXSegmentedViewListContainer
public func contentScrollView() -> UIScrollView {
return scrollView
}
public func scrolling(from leftIndex: Int, to rightIndex: Int, percent: CGFloat, selectedIndex: Int) {
}
public func didClickSelectedItem(at index: Int) {
guard checkIndexValid(index) else {
return
}
willAppearIndex = -1
willDisappearIndex = -1
if currentIndex != index {
listWillDisappear(at: currentIndex, isSystemTrigger: false)
listWillAppear(at: index, isSystemTrigger: false)
listDidDisappear(at: currentIndex, isSystemTrigger: false)
listDidAppear(at: index, isSystemTrigger: false)
}
}
public func reloadData() {
guard let dataSource = dataSource else { return }
if currentIndex < 0 || currentIndex >= dataSource.numberOfLists(in: self) {
defaultSelectedIndex = 0
currentIndex = 0
}
validListDict.values.forEach { (list) in
if let listVC = list as? UIViewController {
listVC.removeFromParent()
}
list.listView().removeFromSuperview()
}
validListDict.removeAll()
if type == .scrollView {
scrollView.contentSize = CGSize(width: scrollView.bounds.size.width*CGFloat(dataSource.numberOfLists(in: self)), height: scrollView.bounds.size.height)
}else {
collectionView.reloadData()
}
listWillAppear(at: currentIndex, isSystemTrigger: false)
listDidAppear(at: currentIndex, isSystemTrigger: false)
}
//MARK: - Private
func initListIfNeeded(at index: Int) {
guard let dataSource = dataSource else { return }
if dataSource.listContainerView?(self, canInitListAt: index) == false {
return
}
var existedList = validListDict[index]
if existedList != nil {
//列表已经创建好了
return
}
existedList = dataSource.listContainerView(self, initListAt: index)
guard let list = existedList else {
return
}
if let vc = list as? UIViewController {
containerVC.addChild(vc)
}
validListDict[index] = list
switch type {
case .scrollView:
list.listView().frame = CGRect(x: CGFloat(index)*scrollView.bounds.size.width, y: 0, width: scrollView.bounds.size.width, height: scrollView.bounds.size.height)
scrollView.addSubview(list.listView())
case .collectionView:
if let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) {
cell.contentView.subviews.forEach { $0.removeFromSuperview() }
list.listView().frame = cell.contentView.bounds
cell.contentView.addSubview(list.listView())
}
}
}
private func listWillAppear(at index: Int, isSystemTrigger: Bool) {
guard let dataSource = dataSource else { return }
guard checkIndexValid(index) else {
return
}
var existedList = validListDict[index]
if existedList != nil {
existedList?.listWillAppear?(isSystemTrigger: isSystemTrigger)
if let vc = existedList as? UIViewController {
vc.beginAppearanceTransition(true, animated: false)
}
}else {
//当前列表未被创建(页面初始化或通过点击触发的listWillAppear)
guard dataSource.listContainerView?(self, canInitListAt: index) != false else {
return
}
existedList = dataSource.listContainerView(self, initListAt: index)
guard let list = existedList else {
return
}
if let vc = list as? UIViewController {
containerVC.addChild(vc)
}
validListDict[index] = list
if type == .scrollView {
if list.listView().superview == nil {
list.listView().frame = CGRect(x: CGFloat(index)*scrollView.bounds.size.width, y: 0, width: scrollView.bounds.size.width, height: scrollView.bounds.size.height)
scrollView.addSubview(list.listView())
}
list.listWillAppear?(isSystemTrigger: isSystemTrigger)
if let vc = list as? UIViewController {
vc.beginAppearanceTransition(true, animated: false)
}
}else {
let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0))
cell?.contentView.subviews.forEach { $0.removeFromSuperview() }
list.listView().frame = cell?.contentView.bounds ?? CGRect.zero
cell?.contentView.addSubview(list.listView())
list.listWillAppear?(isSystemTrigger: isSystemTrigger)
if let vc = list as? UIViewController {
vc.beginAppearanceTransition(true, animated: false)
}
}
}
}
private func listDidAppear(at index: Int, isSystemTrigger: Bool) {
guard checkIndexValid(index) else {
return
}
currentIndex = index
let list = validListDict[index]
list?.listDidAppear?(isSystemTrigger: isSystemTrigger)
if let vc = list as? UIViewController {
vc.endAppearanceTransition()
}
delegate?.listContainerView?(self, listDidAppearAt: index)
}
private func listWillDisappear(at index: Int, isSystemTrigger: Bool) {
guard checkIndexValid(index) else {
return
}
let list = validListDict[index]
list?.listWillDisappear?(isSystemTrigger: isSystemTrigger)
if let vc = list as? UIViewController {
vc.beginAppearanceTransition(false, animated: false)
}
}
private func listDidDisappear(at index: Int, isSystemTrigger: Bool) {
guard checkIndexValid(index) else {
return
}
let list = validListDict[index]
list?.listDidDisappear?(isSystemTrigger: isSystemTrigger)
if let vc = list as? UIViewController {
vc.endAppearanceTransition()
}
}
private func checkIndexValid(_ index: Int) -> Bool {
guard let dataSource = dataSource else { return false }
let count = dataSource.numberOfLists(in: self)
if count <= 0 || index >= count {
return false
}
return true
}
private func listDidAppearOrDisappear(scrollView: UIScrollView) {
let currentIndexPercent = scrollView.contentOffset.x/scrollView.bounds.size.width
if willAppearIndex != -1 || willDisappearIndex != -1 {
let disappearIndex = willDisappearIndex
let appearIndex = willAppearIndex
if willAppearIndex > willDisappearIndex {
//将要出现的列表在右边
if currentIndexPercent >= CGFloat(willAppearIndex) {
willDisappearIndex = -1
willAppearIndex = -1
listDidDisappear(at: disappearIndex, isSystemTrigger: false)
listDidAppear(at: appearIndex, isSystemTrigger: false)
}
}else {
//将要出现的列表在左边
if currentIndexPercent <= CGFloat(willAppearIndex) {
willDisappearIndex = -1
willAppearIndex = -1
listDidDisappear(at: disappearIndex, isSystemTrigger: false)
listDidAppear(at: appearIndex, isSystemTrigger: false)
}
}
}
}
}
extension JXPagingListContainerView: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let dataSource = dataSource else { return 0 }
return dataSource.numberOfLists(in: self)
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.contentView.backgroundColor = listCellBackgroundColor
cell.contentView.subviews.forEach { $0.removeFromSuperview() }
let list = validListDict[indexPath.item]
if list != nil {
if list is UIViewController {
list?.listView().frame = cell.contentView.bounds
}else {
list?.listView().frame = cell.bounds
}
cell.contentView.addSubview(list!.listView())
}
return cell
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return bounds.size
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
delegate?.listContainerViewDidScroll?(self)
guard scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating else {
return
}
let percent = scrollView.contentOffset.x/scrollView.bounds.size.width
let maxCount = Int(round(scrollView.contentSize.width/scrollView.bounds.size.width))
var leftIndex = Int(floor(Double(percent)))
leftIndex = max(0, min(maxCount - 1, leftIndex))
let rightIndex = leftIndex + 1;
if percent < 0 || rightIndex >= maxCount {
listDidAppearOrDisappear(scrollView: scrollView)
return
}
let remainderRatio = percent - CGFloat(leftIndex)
if rightIndex == currentIndex {
//当前选中的在右边,用户正在从右边往左边滑动
if validListDict[leftIndex] == nil && remainderRatio < (1 - initListPercent) {
initListIfNeeded(at: leftIndex)
}else if validListDict[leftIndex] != nil {
if willAppearIndex == -1 {
willAppearIndex = leftIndex;
listWillAppear(at: willAppearIndex, isSystemTrigger: false)
}
}
if willDisappearIndex == -1 {
willDisappearIndex = rightIndex
listWillDisappear(at: willDisappearIndex, isSystemTrigger: false)
}
}else {
//当前选中的在左边,用户正在从左边往右边滑动
if validListDict[rightIndex] == nil && remainderRatio > initListPercent {
initListIfNeeded(at: rightIndex)
}else if validListDict[rightIndex] != nil {
if willAppearIndex == -1 {
willAppearIndex = rightIndex
listWillAppear(at: willAppearIndex, isSystemTrigger: false)
}
}
if willDisappearIndex == -1 {
willDisappearIndex = leftIndex
listWillDisappear(at: willDisappearIndex, isSystemTrigger: false)
}
}
listDidAppearOrDisappear(scrollView: scrollView)
}
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
//滑动到一半又取消滑动处理
if willAppearIndex != -1 || willDisappearIndex != -1 {
listWillDisappear(at: willAppearIndex, isSystemTrigger: false)
listWillAppear(at: willDisappearIndex, isSystemTrigger: false)
listDidDisappear(at: willAppearIndex, isSystemTrigger: false)
listDidAppear(at: willDisappearIndex, isSystemTrigger: false)
willDisappearIndex = -1
willAppearIndex = -1
}
delegate?.listContainerViewDidEndScrolling?(self)
}
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
delegate?.listContainerViewWillBeginDragging?(self)
}
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
delegate?.listContainerViewDidEndScrolling?(self)
}
}
public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
delegate?.listContainerViewDidEndScrolling?(self)
}
}
class JXPagingListContainerViewController: UIViewController {
var viewWillAppearClosure: (()->())?
var viewDidAppearClosure: (()->())?
var viewWillDisappearClosure: (()->())?
var viewDidDisappearClosure: (()->())?
override var shouldAutomaticallyForwardAppearanceMethods: Bool { return false }
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
viewWillAppearClosure?()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
viewDidAppearClosure?()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
viewWillDisappearClosure?()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
viewDidDisappearClosure?()
}
}
class JXPagingListContainerScrollView: UIScrollView, UIGestureRecognizerDelegate {
var isCategoryNestPagingEnabled = false
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if isCategoryNestPagingEnabled, let panGestureClass = NSClassFromString("UIScrollViewPanGestureRecognizer"), gestureRecognizer.isMember(of: panGestureClass) {
let panGesture = gestureRecognizer as! UIPanGestureRecognizer
let velocityX = panGesture.velocity(in: panGesture.view!).x
if velocityX > 0 {
//当前在第一个页面,且往左滑动,就放弃该手势响应,让外层接收,达到多个PagingView左右切换效果
if contentOffset.x == 0 {
return false
}
}else if velocityX < 0 {
//当前在最后一个页面,且往右滑动,就放弃该手势响应,让外层接收,达到多个PagingView左右切换效果
if contentOffset.x + bounds.size.width == contentSize.width {
return false
}
}
}
return true
}
}
class JXPagingListContainerCollectionView: UICollectionView, UIGestureRecognizerDelegate {
var isCategoryNestPagingEnabled = false
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if isCategoryNestPagingEnabled, let panGestureClass = NSClassFromString("UIScrollViewPanGestureRecognizer"), gestureRecognizer.isMember(of: panGestureClass) {
let panGesture = gestureRecognizer as! UIPanGestureRecognizer
let velocityX = panGesture.velocity(in: panGesture.view!).x
if velocityX > 0 {
//当前在第一个页面,且往左滑动,就放弃该手势响应,让外层接收,达到多个PagingView左右切换效果
if contentOffset.x == 0 {
return false
}
}else if velocityX < 0 {
//当前在最后一个页面,且往右滑动,就放弃该手势响应,让外层接收,达到多个PagingView左右切换效果
if contentOffset.x + bounds.size.width == contentSize.width {
return false
}
}
}
return true
}
}