-
Notifications
You must be signed in to change notification settings - Fork 0
/
HWTableView.swift
323 lines (267 loc) · 13.2 KB
/
HWTableView.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
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
//
// HWTableViewVer2.swift
// MyTableViewTest
//
// Created by hanwe lee on 2020/09/29.
// Copyright © 2020 hanwe. All rights reserved.
//
import UIKit
import SkeletonView
import SnapKit
@objc protocol HWTableViewDelegate:class {
@objc optional func hwTableView(_ hwTableVIew: HWTableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
@objc optional func hwTableView(_ hwTableVIew: HWTableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath)
@objc optional func hwTableView(_ hwTableVIew: HWTableView, didSelectRowAt indexPath: IndexPath)
@objc optional func scrollViewDidScroll(_ scrollView: UIScrollView)
@objc optional func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView)
@objc optional func scrollViewWillBeginDragging(_ scrollView: UIScrollView)
@objc optional func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
@objc optional func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool)
@objc optional func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
@objc optional func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView)
@objc optional func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool
@objc optional func scrollViewDidScrollToTop(_ scrollView: UIScrollView)
@objc optional func callNextPage(_ scrollView:UIScrollView)
}
@objc protocol HWTableViewDatasource:class {
func hwTableView(_ hwTableView: HWTableView, numberOfRowsInSection section: Int) -> Int
func hwTableView(_ hwTableView: HWTableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
func hwTableViewSekeletonViewCellIdentifier(_ hwTableView: HWTableView) -> String
func hwTableViewSekeletonViewHeight(_ hwTableView: HWTableView) -> CGFloat
@objc optional func hwTableViewSekeletonViewCount(_ hwTableView: HWTableView) -> Int
@objc optional func hwTableView(_ hwtableView: HWTableView, heightForRowAt indexPath:IndexPath) -> CGFloat
@objc optional func numberOfSections(in hwtableView: HWTableView) -> Int
@objc optional func hwTableView(_ hwtableView: HWTableView, viewForHeaderInSection section: Int) -> UIView?
@objc optional func hwTableView(_ hwtableView: HWTableView, heightForHeaderInSection section: Int) -> CGFloat
@objc optional func hwTableView(_ hwtableView: HWTableView, viewForFooterInSection section: Int) -> UIView?
@objc optional func hwTableView(_ hwtableView: HWTableView, heightForFooterInSection section: Int) -> CGFloat
}
class HWTableView: UIView {
//MARK: public property
public weak var delegate:HWTableViewDelegate?
public weak var dataSource:HWTableViewDatasource?
public lazy var tableView:UITableView = UITableView(frame: self.bounds)
public lazy var separatorStyle:UITableViewCell.SeparatorStyle = self.tableView.separatorStyle {
didSet {
self.tableView.separatorStyle = self.separatorStyle
}
}
public var callNextPageBeforeOffset:CGFloat = 150
public var minimumSkeletonSecond:CGFloat = 0.5
//MARK: private property
private var isShowDisplayAnimation:Bool = true
private var isShowingSkeletonView:Bool = false
private var isOverMinimumSkeletionTimer:Bool = true
private let defaultCellHeight:CGFloat = 100
private var numberOfRows:UInt = 0
private var noResultView:UIView?
private var minimumSkeletionTimer: Timer?
//MARK: lifeCycle
override func awakeFromNib() {
super.awakeFromNib()
initUI()
}
override func layoutSubviews() {
super.layoutSubviews()
}
//MARK: private func
private func initUI() {
self.tableView.backgroundColor = .clear
self.addSubview(self.tableView)
self.tableView.snp.makeConstraints{ (make) in
make.leading.equalTo(self.snp.leading).offset(0)
make.trailing.equalTo(self.snp.trailing).offset(0)
make.top.equalTo(self.snp.top).offset(0)
make.bottom.equalTo(self.snp.bottom).offset(0)
}
self.tableView.delegate = self
self.tableView.dataSource = self
self.isSkeletonable = true
}
private func getSkeletonCellBestCount(cellHeight:CGFloat) -> Int {
var result:Int = 0
result = Int(self.bounds.height/cellHeight)
if self.bounds.height.truncatingRemainder(dividingBy: cellHeight) != 0 {
result += 1
}
return result
}
@objc private func minimumSkeletionTimerCallback() {
self.isOverMinimumSkeletionTimer = true
}
//MARK: public func
public func showSkeletonHW() {
self.isOverMinimumSkeletionTimer = false
DispatchQueue.main.async { [weak self] in
if self?.minimumSkeletionTimer?.isValid ?? false {
self?.minimumSkeletionTimer?.invalidate()
}
self?.minimumSkeletionTimer = Timer.scheduledTimer(timeInterval: TimeInterval(self!.minimumSkeletonSecond), target: self!, selector: #selector(self!.minimumSkeletionTimerCallback), userInfo: nil, repeats: false)
self?.isShowDisplayAnimation = false
self?.tableView.isSkeletonable = true
self?.showAnimatedGradientSkeleton()
self?.startSkeletonAnimation()
self?.isShowingSkeletonView = true
}
}
public func hideSkeletonHW() {
if self.isOverMinimumSkeletionTimer {
DispatchQueue.main.async { [weak self] in
self?.stopSkeletonAnimation()
self?.hideSkeleton()
self?.tableView.reloadData() //리로드를 안해주면 데이터가 이상하게 set된다 ㅡㅡ; skeletonview 버그인듯
self?.isShowingSkeletonView = false
self?.isShowDisplayAnimation = true
}
}
else {
DispatchQueue.global(qos: .default).async { [weak self] in
usleep(3 * 100 * 1000)
DispatchQueue.main.async { [weak self] in
self?.hideSkeletonHW()
}
}
}
}
public func addNoResultView(_ view:UIView) {
self.noResultView = view
guard let subView = self.noResultView else { return }
self.addSubview(subView)
subView.superview?.bringSubviewToFront(subView)
subView.snp.makeConstraints{ (make) in
make.leading.equalTo(self.snp.leading).offset(0)
make.trailing.equalTo(self.snp.trailing).offset(0)
make.top.equalTo(self.snp.top).offset(0)
make.bottom.equalTo(self.snp.bottom).offset(0)
}
subView.isHidden = true
}
public func removeNoResultView() {
self.noResultView = nil
}
public func showNoResultView(completion:(() -> ())?) {
DispatchQueue.main.async { [weak self] in
self?.noResultView?.isHidden = false
self?.isShowDisplayAnimation = true
completion?()
}
}
public func hideNoResultView(completion:(() -> ())?) {
DispatchQueue.main.async { [weak self] in
self?.noResultView?.isHidden = true
self?.isShowDisplayAnimation = true
completion?()
}
}
//MARK: public func for tableView
public func register(_ nib: UINib?, forCellReuseIdentifier identifier: String) {
self.tableView.register(nib, forCellReuseIdentifier: identifier)
}
public func reloadData() {
self.tableView.reloadData()
}
public func dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCell {
return self.tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
}
}
extension HWTableView:UITableViewDelegate {
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if !self.isShowingSkeletonView && self.isShowDisplayAnimation {
cell.transform = CGAffineTransform(translationX: 0, y: 100 * 1.0)
cell.alpha = 0
UIView.animate(
withDuration: 0.5,
delay: 0 * Double(indexPath.row),
options: [.curveEaseInOut],
animations: {
cell.transform = CGAffineTransform(translationX: 0, y: 0)
cell.alpha = 1
})
}
self.delegate?.hwTableView?(self, willDisplay: cell, forRowAt: indexPath)
}
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if !self.isShowingSkeletonView && self.isShowDisplayAnimation {
self.isShowDisplayAnimation = false
}
self.delegate?.hwTableView?(self, didEndDisplaying: cell, forRowAt: indexPath)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.delegate?.hwTableView?(self, didSelectRowAt: indexPath)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.delegate?.scrollViewDidScroll?(scrollView)
let offset = scrollView.contentOffset;
let bounds = scrollView.bounds;
let size = scrollView.contentSize;
let inset = scrollView.contentInset;
let y = offset.y + bounds.size.height - inset.bottom;
let h = size.height;
if y + self.callNextPageBeforeOffset >= h {
self.delegate?.callNextPage?(scrollView)
}
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
self.delegate?.scrollViewDidEndScrollingAnimation?(scrollView)
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.delegate?.scrollViewWillBeginDragging?(scrollView)
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
self.delegate?.scrollViewWillEndDragging?(scrollView, withVelocity: velocity, targetContentOffset: targetContentOffset)
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
self.delegate?.scrollViewDidEndDragging?(scrollView, willDecelerate: decelerate)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.delegate?.scrollViewDidEndDecelerating?(scrollView)
}
func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
self.delegate?.scrollViewWillBeginDecelerating?(scrollView)
}
func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
return self.delegate?.scrollViewShouldScrollToTop?(scrollView) ?? false
}
func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
self.delegate?.scrollViewDidScrollToTop?(scrollView)
}
}
extension HWTableView:SkeletonTableViewDataSource {
func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return self.dataSource?.hwTableViewSekeletonViewCellIdentifier(self) ?? ""
}
func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int {
let cellHight:CGFloat = self.dataSource?.hwTableViewSekeletonViewHeight(self) ?? 0
return self.dataSource?.hwTableViewSekeletonViewCount?(self) ?? self.getSkeletonCellBestCount(cellHeight: cellHight)
}
}
extension HWTableView:UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return self.dataSource?.numberOfSections?(in: self) ?? 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let ds = self.dataSource else { return 0 }
self.numberOfRows = UInt(ds.hwTableView(self, numberOfRowsInSection: section))
return Int(self.numberOfRows)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.dataSource!.hwTableView(self, cellForRowAt: indexPath)
// cell.hideSkeleton()
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.dataSource?.hwTableView?(self, heightForRowAt: indexPath) ?? self.defaultCellHeight
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return self.dataSource?.hwTableView?(self, viewForHeaderInSection: section) ?? nil
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return self.dataSource?.hwTableView?(self, heightForHeaderInSection: section) ?? 0
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return self.dataSource?.hwTableView?(self, viewForFooterInSection: section) ?? nil
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return self.dataSource?.hwTableView?(self, heightForFooterInSection: section) ?? 0
}
}