123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- //
- // ESRefreshComponent.swift
- //
- // Created by egg swift on 16/4/7.
- // Copyright (c) 2013-2016 ESPullToRefresh (https://github.com/eggswift/pull-to-refresh)
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- import Foundation
- import UIKit
- public typealias ESRefreshHandler = (() -> ())
- open class ESRefreshComponent: UIView {
-
- open weak var scrollView: UIScrollView?
-
- /// @param handler Refresh callback method
- open var handler: ESRefreshHandler?
-
- /// @param animator Animated view refresh controls, custom must comply with the following two protocol
- open var animator: (ESRefreshProtocol & ESRefreshAnimatorProtocol)!
-
- /// @param refreshing or not
- fileprivate var _isRefreshing = false
- open var isRefreshing: Bool {
- get {
- return self._isRefreshing
- }
- }
-
- /// @param auto refreshing or not
- fileprivate var _isAutoRefreshing = false
- open var isAutoRefreshing: Bool {
- get {
- return self._isAutoRefreshing
- }
- }
-
- /// @param tag observing
- fileprivate var isObservingScrollView = false
- fileprivate var isIgnoreObserving = false
- public override init(frame: CGRect) {
- super.init(frame: frame)
- autoresizingMask = [.flexibleLeftMargin, .flexibleWidth, .flexibleRightMargin]
- }
-
- public convenience init(frame: CGRect, handler: @escaping ESRefreshHandler) {
- self.init(frame: frame)
- self.handler = handler
- self.animator = ESRefreshAnimator.init()
- }
-
- public convenience init(frame: CGRect, handler: @escaping ESRefreshHandler, animator: ESRefreshProtocol & ESRefreshAnimatorProtocol) {
- self.init(frame: frame)
- self.handler = handler
- self.animator = animator
- }
-
- public required init?(coder aDecoder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- deinit {
- removeObserver()
- }
-
- open override func willMove(toSuperview newSuperview: UIView?) {
- super.willMove(toSuperview: newSuperview)
- /// Remove observer from superview immediately
- self.removeObserver()
- DispatchQueue.main.async { [weak self, newSuperview] in
- /// Add observer to new superview in next runloop
- self?.addObserver(newSuperview)
- }
- }
-
- open override func didMoveToSuperview() {
- super.didMoveToSuperview()
- self.scrollView = self.superview as? UIScrollView
- if let _ = animator {
- let v = animator.view
- if v.superview == nil {
- let inset = animator.insets
- self.addSubview(v)
- v.frame = CGRect.init(x: inset.left,
- y: inset.right,
- width: self.bounds.size.width - inset.left - inset.right,
- height: self.bounds.size.height - inset.top - inset.bottom)
- v.autoresizingMask = [
- .flexibleWidth,
- .flexibleTopMargin,
- .flexibleHeight,
- .flexibleBottomMargin
- ]
- }
- }
- }
-
- // MARK: - Action
-
- public final func startRefreshing(isAuto: Bool = false) -> Void {
- guard isRefreshing == false && isAutoRefreshing == false else {
- return
- }
-
- _isRefreshing = !isAuto
- _isAutoRefreshing = isAuto
-
- self.start()
- }
-
- public final func stopRefreshing() -> Void {
- guard isRefreshing == true || isAutoRefreshing == true else {
- return
- }
-
- self.stop()
- }
-
- public func start() {
-
- }
-
- public func stop() {
- _isRefreshing = false
- _isAutoRefreshing = false
- }
-
- // ScrollView contentSize change action
- public func sizeChangeAction(object: AnyObject?, change: [NSKeyValueChangeKey : Any]?) {
-
- }
-
- // ScrollView offset change action
- public func offsetChangeAction(object: AnyObject?, change: [NSKeyValueChangeKey : Any]?) {
-
- }
-
- }
- extension ESRefreshComponent /* KVO methods */ {
-
- fileprivate static var context = "ESRefreshKVOContext"
- fileprivate static let offsetKeyPath = "contentOffset"
- fileprivate static let contentSizeKeyPath = "contentSize"
-
- public func ignoreObserver(_ ignore: Bool = false) {
- if let scrollView = scrollView {
- scrollView.isScrollEnabled = !ignore
- }
- isIgnoreObserving = ignore
- }
-
- fileprivate func addObserver(_ view: UIView?) {
- if let scrollView = view as? UIScrollView, !isObservingScrollView {
- scrollView.addObserver(self, forKeyPath: ESRefreshComponent.offsetKeyPath, options: [.initial, .new], context: &ESRefreshComponent.context)
- scrollView.addObserver(self, forKeyPath: ESRefreshComponent.contentSizeKeyPath, options: [.initial, .new], context: &ESRefreshComponent.context)
- isObservingScrollView = true
- }
- }
-
- fileprivate func removeObserver() {
- if let scrollView = superview as? UIScrollView, isObservingScrollView {
- scrollView.removeObserver(self, forKeyPath: ESRefreshComponent.offsetKeyPath, context: &ESRefreshComponent.context)
- scrollView.removeObserver(self, forKeyPath: ESRefreshComponent.contentSizeKeyPath, context: &ESRefreshComponent.context)
- isObservingScrollView = false
- }
- }
-
- override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
- if context == &ESRefreshComponent.context {
- guard isUserInteractionEnabled == true && isHidden == false else {
- return
- }
- if keyPath == ESRefreshComponent.contentSizeKeyPath {
- if isIgnoreObserving == false {
- sizeChangeAction(object: object as AnyObject?, change: change)
- }
- } else if keyPath == ESRefreshComponent.offsetKeyPath {
- if isIgnoreObserving == false {
- offsetChangeAction(object: object as AnyObject?, change: change)
- }
- }
- } else {
- }
- }
-
- }
|