diff --git a/PullToRefresh/PullToRefresh.swift b/PullToRefresh/PullToRefresh.swift index 4f022e0..97659f9 100644 --- a/PullToRefresh/PullToRefresh.swift +++ b/PullToRefresh/PullToRefresh.swift @@ -56,6 +56,8 @@ open class PullToRefresh: NSObject { fileprivate let animator: RefreshViewAnimator fileprivate var isObserving = false + fileprivate var haveFinished = false + // MARK: - ScrollView & Observing fileprivate var scrollViewDefaultInsets: UIEdgeInsets = .zero @@ -77,9 +79,15 @@ open class PullToRefresh: NSObject { didSet { animator.animate(state) switch state { - case .loading: - if oldValue != .loading { - animateLoadingState() + case .loading(let isDragging): + if oldValue != state { + if isDragging == false { + animateLoadingState() + if oldValue == .loading(isDragging: true) { + break//don't perform action twice + } + } + action?() } case .finished: @@ -153,19 +161,26 @@ extension PullToRefresh { } } let refreshViewHeight = refreshView.frame.size.height - - switch offset { - case 0 where (state != .loading): state = .initial - case -refreshViewHeight...0 where (state != .loading && state != .finished): - state = .releasing(progress: -offset / refreshViewHeight) - - case -1000...(-refreshViewHeight): - if state == .releasing(progress: 1) && scrollView?.isDragging == false { - state = .loading - } else if state != .loading && state != .finished { - state = .releasing(progress: 1) + if haveFinished && scrollView?.isDragging == false && (state == .loading(isDragging: false) || state == .loading(isDragging: true)) { + haveFinished = false + state = .finished + } else if state == .loading(isDragging: true) && scrollView?.isDragging == false { + state = .loading(isDragging: false) + } else { + switch offset { + case -5...0 where state != .loading(isDragging: false): + state = .initial + case -refreshViewHeight...0 where (state != .finished && state != .loading(isDragging: false) && state != .loading(isDragging: true)): + state = .releasing(progress: -offset / refreshViewHeight) + + case -1000...(-refreshViewHeight): + if state == .releasing(progress: 1) { + state = .loading(isDragging: true) + } else if state != .finished && state != .loading(isDragging: false) && state != .loading(isDragging: true) { + state = .releasing(progress: 1) + } + default: break } - default: break } } else if (context == &KVO.context && keyPath == KVO.ScrollViewPath.contentSize && object as? UIScrollView == scrollView) { if case .bottom = position { @@ -227,13 +242,18 @@ extension PullToRefresh { scrollView?.setContentOffset(CGPoint(x: 0, y: offsetY), animated: true) let delayTime = DispatchTime.now() + Double(Int64(0.27 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) DispatchQueue.main.asyncAfter(deadline: delayTime) { [weak self] in - self?.state = .loading + self?.state = .loading(isDragging: false) } } func endRefreshing() { - if state == .loading { - state = .finished + if state == .loading(isDragging: false) || state == .loading(isDragging: true) { + if scrollView?.isDragging == false { + state = .finished + haveFinished = false + } else { + haveFinished = true + } } } } @@ -255,7 +275,7 @@ private extension PullToRefresh { case .top: let insets = self.refreshView.frame.height + self.scrollViewDefaultInsets.top scrollView.contentInset.top = insets - let offsetY = self.defaultInsets.top + self.refreshView.frame.height + let offsetY = self.scrollViewDefaultInsets.top + self.refreshView.frame.height scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: -offsetY) case .bottom: @@ -270,8 +290,6 @@ private extension PullToRefresh { } } ) - - action?() } func animateFinishedState() { diff --git a/PullToRefresh/State.swift b/PullToRefresh/State.swift old mode 100644 new mode 100755 index fac13f5..94c56f8 --- a/PullToRefresh/State.swift +++ b/PullToRefresh/State.swift @@ -12,14 +12,14 @@ public enum State: Equatable, CustomStringConvertible { case initial case releasing(progress: CGFloat) - case loading + case loading(isDragging: Bool) case finished public var description: String { switch self { case .initial: return "Inital" - case .releasing(let progress): return "Releasing:\(progress)" - case .loading: return "Loading" + case .releasing(let progress): return "Releasing: \(progress)" + case .loading(let isDragging): return "Loading: \(isDragging)" case .finished: return "Finished" } } @@ -28,9 +28,9 @@ public enum State: Equatable, CustomStringConvertible { public func ==(a: State, b: State) -> Bool { switch (a, b) { case (.initial, .initial): return true - case (.loading, .loading): return true case (.finished, .finished): return true - case (.releasing, .releasing): return true + case (.loading(let isDragging1), .loading(let isDragging2)) where isDragging1 == isDragging2: return true + case (.releasing(let progress1), .releasing(let progress2)) where abs(progress1 - progress2) < 0.01: return true default: return false } } diff --git a/PullToRefresh/UIScrollView+PullToRefresh.swift b/PullToRefresh/UIScrollView+PullToRefresh.swift old mode 100644 new mode 100755 index 37f7e4b..e1a134d --- a/PullToRefresh/UIScrollView+PullToRefresh.swift +++ b/PullToRefresh/UIScrollView+PullToRefresh.swift @@ -67,7 +67,7 @@ public extension UIScrollView { sendSubview(toBack: view) } - func removePullToRefresh(at position: Position) { + func removePullToRefresh(at position: Position = .top) { switch position { case .top: topPullToRefresh?.refreshView.removeFromSuperview() @@ -84,7 +84,7 @@ public extension UIScrollView { removePullToRefresh(at: .bottom) } - func startRefreshing(at position: Position) { + func startRefreshing(at position: Position = .top) { switch position { case .top: topPullToRefresh?.startRefreshing() @@ -94,7 +94,7 @@ public extension UIScrollView { } } - func endRefreshing(at position: Position) { + func endRefreshing(at position: Position = .top) { switch position { case .top: topPullToRefresh?.endRefreshing()