Skip to content

Commit 1957ef9

Browse files
author
Sergey D
authored
Merge pull request #96 from Yalantis/develop
Develop
2 parents f0b77ce + 01852b0 commit 1957ef9

File tree

2 files changed

+88
-28
lines changed

2 files changed

+88
-28
lines changed

PullToRefresh/PullToRefresh.swift

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ import UIKit
1111
public enum Position {
1212

1313
case top, bottom
14+
15+
var opposite: Position {
16+
switch self {
17+
case .top:
18+
return .bottom
19+
case .bottom:
20+
return .top
21+
}
22+
}
23+
1424
}
1525

1626
open class PullToRefresh: NSObject {
@@ -25,7 +35,17 @@ open class PullToRefresh: NSObject {
2535
open var shouldBeVisibleWhileScrolling: Bool = false
2636

2737
let refreshView: UIView
28-
var action: (() -> ())?
38+
var isEnabled: Bool = false {
39+
didSet{
40+
refreshView.isHidden = !isEnabled
41+
if isEnabled {
42+
addScrollViewObserving()
43+
} else {
44+
removeScrollViewObserving()
45+
}
46+
}
47+
}
48+
var action: (() -> Void)?
2949

3050
weak var scrollView: UIScrollView? {
3151
willSet {
@@ -74,9 +94,12 @@ open class PullToRefresh: NSObject {
7494
scrollView?.contentInset = self.scrollViewDefaultInsets
7595
state = .initial
7696
}
77-
97+
case .releasing(progress: let value) where value < 0.1:
98+
state = .initial
99+
78100
default: break
79101
}
102+
self.enableOppositeRefresher(state == .initial)
80103
}
81104
}
82105

@@ -192,18 +215,30 @@ extension PullToRefresh {
192215
extension PullToRefresh {
193216

194217
func startRefreshing() {
195-
guard state == .initial, let scrollView = scrollView else {
218+
guard !isOppositeRefresherLoading, state == .initial, let scrollView = scrollView else {
196219
return
197220
}
221+
222+
let topInset: CGFloat = {
223+
if #available(iOS 11, *) {
224+
return scrollView.safeAreaInsets.top
225+
}
226+
return 0
227+
}()
228+
198229
var offsetY: CGFloat
199230
switch position {
200231
case .top:
201-
offsetY = -refreshView.frame.height - scrollViewDefaultInsets.top
202-
if #available(iOS 11, *) {
203-
offsetY -= scrollView.safeAreaInsets.top
204-
}
232+
offsetY = -refreshView.frame.height - scrollViewDefaultInsets.top - topInset
205233
case .bottom:
206-
offsetY = scrollView.contentSize.height + refreshView.frame.height + scrollViewDefaultInsets.bottom - scrollView.bounds.height
234+
if scrollView.contentSize.height + refreshView.frame.height > scrollView.frame.height {
235+
offsetY = scrollView.contentSize.height
236+
+ refreshView.frame.height
237+
+ scrollViewDefaultInsets.bottom
238+
- scrollView.bounds.height
239+
} else {
240+
offsetY = 0 - topInset
241+
}
207242
}
208243
state = .loading
209244
scrollView.setContentOffset(CGPoint(x: 0, y: offsetY), animated: true)
@@ -219,8 +254,21 @@ extension PullToRefresh {
219254
// MARK: - Animate scroll view
220255
private extension PullToRefresh {
221256

257+
var isOppositeRefresherLoading: Bool {
258+
guard let scrollView = scrollView, let oppositeRefresher = scrollView.refresher(at: position.opposite) else {
259+
return false
260+
}
261+
return oppositeRefresher.state != .initial
262+
}
263+
264+
func enableOppositeRefresher(_ enable: Bool) {
265+
if let scrollView = scrollView, let oppositeRefresher = scrollView.refresher(at: position.opposite) {
266+
oppositeRefresher.isEnabled = enable
267+
}
268+
}
269+
222270
func animateLoadingState() {
223-
guard let scrollView = scrollView else {
271+
guard !isOppositeRefresherLoading, let scrollView = scrollView else {
224272
return
225273
}
226274

@@ -238,15 +286,14 @@ private extension PullToRefresh {
238286
let insetY = self.refreshView.frame.height + self.scrollViewDefaultInsets.bottom
239287
scrollView.contentInset.bottom = insetY
240288
}
241-
},
289+
},
242290
completion: { _ in
243291
scrollView.bounces = true
244292
if self.shouldBeVisibleWhileScrolling {
245293
self.bringRefreshViewToSuperview()
246294
}
247-
}
295+
}
248296
)
249-
250297
action?()
251298
}
252299

@@ -260,11 +307,11 @@ private extension PullToRefresh {
260307
options: animationOptions,
261308
animations: {
262309
self.scrollView?.contentInset = self.scrollViewDefaultInsets
263-
},
310+
},
264311
completion: { _ in
265312
self.addScrollViewObserving()
266313
self.state = .initial
267-
}
314+
}
268315
)
269316
}
270317
}

PullToRefresh/UIScrollView+PullToRefresh.swift

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,7 @@ public extension UIScrollView {
3333
}
3434
}
3535

36-
internal func defaultFrame(forPullToRefresh pullToRefresh: PullToRefresh) -> CGRect {
37-
let view = pullToRefresh.refreshView
38-
var originY: CGFloat
39-
switch pullToRefresh.position {
40-
case .top:
41-
originY = -view.frame.size.height
42-
case .bottom:
43-
originY = contentSize.height
44-
}
45-
return CGRect(x: 0, y: originY, width: frame.width, height: view.frame.height)
46-
}
47-
48-
public func addPullToRefresh(_ pullToRefresh: PullToRefresh, action: @escaping () -> ()) {
36+
func addPullToRefresh(_ pullToRefresh: PullToRefresh, action: @escaping () -> ()) {
4937
pullToRefresh.scrollView = self
5038
pullToRefresh.action = action
5139

@@ -67,6 +55,16 @@ public extension UIScrollView {
6755
sendSubview(toBack: view)
6856
}
6957

58+
func refresher(at position: Position) -> PullToRefresh? {
59+
switch position {
60+
case .top:
61+
return topPullToRefresh
62+
63+
case .bottom:
64+
return bottomPullToRefresh
65+
}
66+
}
67+
7068
func removePullToRefresh(at position: Position) {
7169
switch position {
7270
case .top:
@@ -108,6 +106,7 @@ public extension UIScrollView {
108106
endRefreshing(at: .top)
109107
endRefreshing(at: .bottom)
110108
}
109+
111110
}
112111

113112
internal func - (lhs: UIEdgeInsets, rhs: UIEdgeInsets) -> UIEdgeInsets {
@@ -119,7 +118,7 @@ internal func - (lhs: UIEdgeInsets, rhs: UIEdgeInsets) -> UIEdgeInsets {
119118
)
120119
}
121120

122-
extension UIScrollView {
121+
internal extension UIScrollView {
123122

124123
var normalizedContentOffset: CGPoint {
125124
get {
@@ -148,4 +147,18 @@ extension UIScrollView {
148147
}
149148
}
150149
}
150+
151+
func defaultFrame(forPullToRefresh pullToRefresh: PullToRefresh) -> CGRect {
152+
let view = pullToRefresh.refreshView
153+
var originY: CGFloat
154+
switch pullToRefresh.position {
155+
case .top:
156+
originY = -view.frame.size.height
157+
case .bottom:
158+
originY = contentSize.height
159+
}
160+
161+
return CGRect(x: 0, y: originY, width: frame.width, height: view.frame.height)
162+
}
163+
151164
}

0 commit comments

Comments
 (0)