Skip to content

Commit e6245c6

Browse files
author
Sergey D
authored
Merge pull request #87 from Yalantis/develop
Merge with Develop branch
2 parents 4d3b2c5 + 369edbd commit e6245c6

File tree

2 files changed

+47
-120
lines changed

2 files changed

+47
-120
lines changed

PullToRefresh/PullToRefresh.swift

Lines changed: 21 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,17 @@ open class PullToRefresh: NSObject {
2222
open var springDamping: CGFloat = 0.4
2323
open var initialSpringVelocity: CGFloat = 0.8
2424
open var animationOptions: UIViewAnimationOptions = [.curveLinear]
25-
open var shouldBeVisibleWhileScrolling: Bool = false {
26-
willSet{
27-
if shouldBeVisibleWhileScrolling {
28-
sendRefreshViewToScrollView()
29-
}
30-
}
31-
}
25+
open var shouldBeVisibleWhileScrolling: Bool = false
3226

3327
let refreshView: UIView
3428
var action: (() -> ())?
3529

3630
weak var scrollView: UIScrollView? {
3731
willSet {
38-
if #available(iOS 11.0, *) {
39-
scrollView?.removeAdjustedContentInsetsHandler(forPosition: position)
40-
}
4132
removeScrollViewObserving()
4233
}
4334
didSet {
4435
if let scrollView = scrollView {
45-
if #available(iOS 11.0, *) {
46-
scrollView.addAdjustedContentInsetsHandler(forPosition: position) { [weak self] (adjustedInsets) in
47-
self?.scrollViewDefaultAdjustedInsets = adjustedInsets
48-
}
49-
}
5036
scrollViewDefaultInsets = scrollView.contentInset
5137
addScrollViewObserving()
5238
}
@@ -59,7 +45,6 @@ open class PullToRefresh: NSObject {
5945
// MARK: - ScrollView & Observing
6046

6147
fileprivate var scrollViewDefaultInsets: UIEdgeInsets = .zero
62-
fileprivate var scrollViewDefaultAdjustedInsets: UIEdgeInsets = .zero
6348
fileprivate var previousScrollViewOffset: CGPoint = CGPoint.zero
6449

6550
// MARK: - State
@@ -135,25 +120,21 @@ extension PullToRefresh {
135120
override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
136121
if (context == &KVO.context && keyPath == KVO.ScrollViewPath.contentOffset && object as? UIScrollView == scrollView) {
137122
var offset: CGFloat
138-
var topInsetY: CGFloat
139-
if #available(iOS 11, *) {
140-
topInsetY = scrollView!.adjustedContentInset.top
141-
} else {
142-
topInsetY = scrollView!.contentInset.top
143-
}
144123
switch position {
145124
case .top:
146-
offset = previousScrollViewOffset.y + topInsetY
125+
offset = previousScrollViewOffset.y + scrollViewDefaultInsets.top
147126

148127
case .bottom:
149128
if scrollView!.contentSize.height > scrollView!.bounds.height {
150-
offset = scrollView!.contentSize.height - previousScrollViewOffset.y - scrollView!.bounds.height + topInsetY
129+
offset = scrollView!.contentSize.height - previousScrollViewOffset.y - scrollView!.bounds.height
151130
} else {
152-
offset = scrollView!.contentSize.height - previousScrollViewOffset.y + topInsetY
131+
offset = scrollView!.contentSize.height - previousScrollViewOffset.y
132+
}
133+
if #available(iOS 11, *) {
134+
offset += scrollView!.safeAreaInsets.top
153135
}
154136
}
155137
let refreshViewHeight = refreshView.frame.size.height
156-
157138
switch offset {
158139
case 0 where (state != .loading): state = .initial
159140
case -refreshViewHeight...0 where (state != .loading && state != .finished):
@@ -178,7 +159,7 @@ extension PullToRefresh {
178159
} else {
179160
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
180161
}
181-
previousScrollViewOffset.y = scrollView?.contentOffset.y ?? 0
162+
previousScrollViewOffset.y = scrollView?.normalizedContentOffset.y ?? 0
182163
}
183164

184165
fileprivate func addScrollViewObserving() {
@@ -211,24 +192,21 @@ extension PullToRefresh {
211192
extension PullToRefresh {
212193

213194
func startRefreshing() {
214-
if self.state != .initial {
195+
guard state == .initial, let scrollView = scrollView else {
215196
return
216197
}
217-
218198
var offsetY: CGFloat
219199
switch position {
220200
case .top:
221201
offsetY = -refreshView.frame.height - scrollViewDefaultInsets.top
222-
202+
if #available(iOS 11, *) {
203+
offsetY -= scrollView.safeAreaInsets.top
204+
}
223205
case .bottom:
224-
offsetY = scrollView!.contentSize.height + refreshView.frame.height + scrollViewDefaultInsets.bottom - scrollView!.bounds.height
225-
}
226-
227-
scrollView?.setContentOffset(CGPoint(x: 0, y: offsetY), animated: true)
228-
let delayTime = DispatchTime.now() + Double(Int64(0.27 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
229-
DispatchQueue.main.asyncAfter(deadline: delayTime) { [weak self] in
230-
self?.state = .loading
206+
offsetY = scrollView.contentSize.height + refreshView.frame.height + scrollViewDefaultInsets.bottom - scrollView.bounds.height
231207
}
208+
state = .loading
209+
scrollView.setContentOffset(CGPoint(x: 0, y: offsetY), animated: true)
232210
}
233211

234212
func endRefreshing() {
@@ -253,14 +231,12 @@ private extension PullToRefresh {
253231
animations: {
254232
switch self.position {
255233
case .top:
256-
let insets = self.refreshView.frame.height + self.scrollViewDefaultInsets.top
257-
scrollView.contentInset.top = insets
258-
let offsetY = self.defaultInsets.top + self.refreshView.frame.height
259-
scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: -offsetY)
260-
234+
let insetY = self.refreshView.frame.height + self.scrollViewDefaultInsets.top
235+
scrollView.contentInset.top = insetY
236+
scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: -insetY)
261237
case .bottom:
262-
let insets = self.refreshView.frame.height + self.scrollViewDefaultInsets.bottom
263-
scrollView.contentInset.bottom = insets
238+
let insetY = self.refreshView.frame.height + self.scrollViewDefaultInsets.bottom
239+
scrollView.contentInset.bottom = insetY
264240
}
265241
},
266242
completion: { _ in
@@ -284,9 +260,6 @@ private extension PullToRefresh {
284260
options: animationOptions,
285261
animations: {
286262
self.scrollView?.contentInset = self.scrollViewDefaultInsets
287-
if case .top = self.position {
288-
self.scrollView?.contentOffset.y = -self.defaultInsets.top
289-
}
290263
},
291264
completion: { _ in
292265
self.addScrollViewObserving()
@@ -299,18 +272,10 @@ private extension PullToRefresh {
299272
// MARK: - Helpers
300273
private extension PullToRefresh {
301274

302-
var defaultInsets: UIEdgeInsets {
303-
if #available(iOS 11, *) {
304-
return scrollViewDefaultAdjustedInsets
305-
} else {
306-
return scrollViewDefaultInsets
307-
}
308-
}
309-
310275
var isCurrentlyVisible: Bool {
311276
guard let scrollView = scrollView else { return false }
312277

313-
return scrollView.contentOffset.y <= -defaultInsets.top
278+
return scrollView.normalizedContentOffset.y <= -scrollViewDefaultInsets.top
314279
}
315280

316281
func bringRefreshViewToSuperview() {
@@ -331,4 +296,3 @@ private extension PullToRefresh {
331296
}
332297

333298
}
334-

PullToRefresh/UIScrollView+PullToRefresh.swift

Lines changed: 26 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -110,79 +110,42 @@ public extension UIScrollView {
110110
}
111111
}
112112

113-
private var topPullToRefreshInsetsHandlerKey: UInt8 = 0
114-
private var bottomPullToRefreshInsetsHandlerKey: UInt8 = 0
115-
private var implementationSwapedKey: UInt8 = 0
113+
internal func - (lhs: UIEdgeInsets, rhs: UIEdgeInsets) -> UIEdgeInsets {
114+
return UIEdgeInsets(
115+
top: lhs.top - rhs.top,
116+
left: lhs.left - rhs.left,
117+
bottom: lhs.bottom - rhs.bottom,
118+
right: lhs.right - rhs.right
119+
)
120+
}
116121

117-
@available(iOS 11.0, *)
118122
extension UIScrollView {
119123

120-
private var topPullToRefreshInsetsHandler: ((UIEdgeInsets) -> Void)? {
124+
var normalizedContentOffset: CGPoint {
121125
get {
122-
return objc_getAssociatedObject(self, &topPullToRefreshInsetsHandlerKey) as? ((UIEdgeInsets) -> Void)
123-
}
124-
set {
125-
objc_setAssociatedObject(self, &topPullToRefreshInsetsHandlerKey, newValue, .OBJC_ASSOCIATION_RETAIN)
126+
let contentOffset = self.contentOffset
127+
let contentInset = self.effectiveContentInset
128+
129+
let output = CGPoint(x: contentOffset.x + contentInset.left, y: contentOffset.y + contentInset.top)
130+
return output
126131
}
127132
}
128133

129-
private var bottomPullToRefreshInsetsHandler: ((UIEdgeInsets) -> Void)? {
134+
var effectiveContentInset: UIEdgeInsets {
130135
get {
131-
return objc_getAssociatedObject(self, &bottomPullToRefreshInsetsHandlerKey) as? ((UIEdgeInsets) -> Void)
136+
if #available(iOS 11, *) {
137+
return adjustedContentInset
138+
} else {
139+
return contentInset
140+
}
132141
}
133-
set {
134-
objc_setAssociatedObject(self, &bottomPullToRefreshInsetsHandlerKey, newValue, .OBJC_ASSOCIATION_RETAIN)
135-
}
136-
}
137-
138-
private var isImplementationSwaped: Bool {
139-
get{
140-
return objc_getAssociatedObject(self, &implementationSwapedKey) as? Bool ?? false
141-
}
142-
set{
143-
objc_setAssociatedObject(self, &implementationSwapedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
144-
}
145-
}
146-
147-
internal func addAdjustedContentInsetsHandler(forPosition position: Position, handler: @escaping ((UIEdgeInsets) -> Void)) {
148-
switch position {
149-
case .top:
150-
topPullToRefreshInsetsHandler = handler
151-
case .bottom:
152-
bottomPullToRefreshInsetsHandler = handler
153-
}
154-
if !isImplementationSwaped {
155-
swapAdjustedContentInsetDidChangeImplementation()
156-
isImplementationSwaped = true
157-
}
158-
}
159-
160-
private func swapAdjustedContentInsetDidChangeImplementation() {
161-
let originalSelector = #selector(adjustedContentInsetDidChange)
162-
let swizzledSelector = #selector(patchedAdjustedContentInsetDidChange)
163142

164-
if let originalMethod = class_getInstanceMethod(UIScrollView.self, originalSelector),
165-
let swizzledMethod = class_getInstanceMethod(UIScrollView.self, swizzledSelector) {
166-
method_exchangeImplementations(originalMethod, swizzledMethod)
167-
}
168-
}
169-
170-
internal func removeAdjustedContentInsetsHandler(forPosition position: Position) {
171-
switch position {
172-
case .top:
173-
topPullToRefreshInsetsHandler = nil
174-
case .bottom:
175-
bottomPullToRefreshInsetsHandler = nil
176-
}
177-
if topPullToRefreshInsetsHandler == nil && bottomPullToRefreshInsetsHandler == nil {
178-
swapAdjustedContentInsetDidChangeImplementation()
179-
isImplementationSwaped = false
143+
set {
144+
if #available(iOS 11.0, *), contentInsetAdjustmentBehavior != .never {
145+
contentInset = newValue - safeAreaInsets
146+
} else {
147+
contentInset = newValue
148+
}
180149
}
181150
}
182-
183-
@objc internal func patchedAdjustedContentInsetDidChange() {
184-
topPullToRefreshInsetsHandler?(adjustedContentInset)
185-
bottomPullToRefreshInsetsHandler?(adjustedContentInset)
186-
patchedAdjustedContentInsetDidChange()
187-
}
188151
}

0 commit comments

Comments
 (0)