Compare commits

..

36 Commits
v0.1 ... master

Author SHA1 Message Date
Evgenii Elchev 26aa5dda8c Merge branch 'swift-4.2' into 'master'
Swift 4.2

See merge request helper-ios/RMRPullToRefresh!5
2018-10-15 16:12:22 +03:00
Evgenii Elchev 756991bc98 Merge branch 'master' into swift-4.2
# Conflicts:
#	Classes/RMRPullToRefreshController.swift
#	Example/RMRPullToRefresh/Base.lproj/Main.storyboard
#	Example/RMRPullToRefresh/ViewController.swift
#	Example/RMRPullToRefreshExample.xcodeproj/project.pbxproj
#	RMRPullToRefresh.podspec
2018-10-15 20:06:08 +07:00
Evgenii Elchev 99d4e48ed1 Поднял версию пода 2018-10-15 19:55:17 +07:00
Jon Fir ba0dfe1489 Поднял версию pod`а 2018-09-21 10:21:28 +07:00
Jon Fir baa205e053 Устранены предупреждения 2018-09-21 10:20:00 +07:00
Jon Fir 4abdd96604 Перевел на swift 4.2 2018-09-20 18:11:23 +07:00
Jon Fir e1ab80365e Выполнен переход на swift 4.2 2018-09-20 16:13:13 +07:00
Mikhail Konovalov 36174af0e8 Поднял версию в подспеке 2018-01-11 13:03:23 +03:00
Mikhail Konovalov 99fa1ae686 Поправил создание констрейнтов 2018-01-11 13:00:36 +03:00
Mikhail Konovalov db5402288b Поднял версию iOS до 9 2018-01-11 13:00:22 +03:00
Mikhail Konovalov 43b8834736 Fileprivate на private 2018-01-11 12:40:23 +03:00
Mikhail Konovalov 59de97d267 Фикс утечки памяти 2018-01-11 12:40:04 +03:00
Mikhail Konovalov 397f6d33b2 Закомментировал задержку скрытия pull to refresh и отображение в случае ошибки 2018-01-11 12:04:41 +03:00
Mikhail Konovalov 788a5ae73c Обновил работу с KVO 2018-01-11 12:03:56 +03:00
Mikhail Konovalov a59d153dbe Поправил форматирование 2018-01-11 11:34:25 +03:00
Mikhail Konovalov 303ac129e6 Мигрировал на Swift 4, поправил предупреждения 2018-01-11 11:25:41 +03:00
Mikhail Konovalov 7c13b59323 Поправил предупреждения cocoapods 2018-01-11 11:15:22 +03:00
Nina Dmitrieva 89f333e3b6 Подняла версию в подспеке до 0.5.0, поправила тег в source 2017-09-05 18:07:33 +03:00
Ivan Vavilov e11049878a Merge branch 'hotfix/remove-warnings' into 'master'
Убрал устаревшее M_PI

See merge request !3
2017-08-28 11:55:26 +03:00
Ivan Vavilov c42cdd6db9 Убрал устаревшее M_PI 2017-08-28 11:54:29 +03:00
Nina Dmitrieva 7940750cea Merge branch 'bugfix/deinit-crash' into 'master'
Фикс краша, возникающего при уничтожении экрана, в котором есть pull-to-refresh

See merge request !2
2017-04-27 16:19:30 +03:00
Nina Dmitrieva 914c1c6285 Увеличила версию podspec до 0.4 2017-04-27 15:44:30 +03:00
Nina Dmitrieva ce35864c72 Добавила .gitignore 2017-04-27 15:44:12 +03:00
Nina Dmitrieva 27562bf786 Фикс краша, возникающего при уничтожении экрана, в котором есть pull-to-refresh. В deinit этого экрана нужно вызывать метод unsubscribeFromBindings, который отпишет вручную наблюдателей на scroll view. 2017-04-27 15:42:57 +03:00
Sergey Germanovich f72ba63c7e Подправил podspec 2017-04-26 17:05:32 +03:00
Andrey Rozhkov ec6a7985e3 Merge branch 'feature/get-state' into 'master'
Открыл свойствo для чтения статуса pullToRefresh

See merge request !1
2017-02-06 18:05:55 +03:00
zhorkov023 39b63b0b40 Увеличил версию podspec 0.3 2017-02-06 16:55:37 +03:00
zhorkov023 2a1c9abd67 Открыл свойства для чтения статуса pullToRefresh 2017-02-06 16:55:15 +03:00
Sergey Germanovich 3b5adda64b Update README.md: обновил адрес репозитория 2017-01-17 14:24:25 +03:00
Olga Vorona 4fb74f3d9b Поправила адрес спецификации 2016-10-14 18:59:01 +03:00
Olga Vorona 4f03af8286 Версия 0.2 2016-10-13 17:52:09 +03:00
Olga Vorona 5525fe0f92 Добавлена лицензия 2016-10-12 16:50:32 +03:00
Olga Vorona 283b293696 Версия пода 0.2 2016-10-11 17:37:31 +03:00
Olga Vorona 2e56e21848 Swift3 Update 2016-10-11 17:22:57 +03:00
Ilya Merkulov 2f8e094c09 Добавлены картинки в readme.md 2016-04-18 12:55:39 +00:00
Ilya Merkulov f4b824d16b Update README.md 2016-04-18 10:58:03 +00:00
26 changed files with 904 additions and 477 deletions

177
.gitignore vendored Normal file
View File

@ -0,0 +1,177 @@
#########################
# .gitignore file for Xcode4 and Xcode5 Source projects
#
# Apple bugs, waiting for Apple to fix/respond:
#
# 15564624 - what does the xccheckout file in Xcode5 do? Where's the documentation?
#
# Version 2.1
# For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects
#
# 2013 updates:
# - fixed the broken "save personal Schemes"
# - added line-by-line explanations for EVERYTHING (some were missing)
#
# NB: if you are storing "built" products, this WILL NOT WORK,
# and you should use a different .gitignore (or none at all)
# This file is for SOURCE projects, where there are many extra
# files that we want to exclude
#
#########################
#####
# OS X temporary files that should never be committed
#
# c.f. http://www.westwind.com/reference/os-x/invisibles.html
.DS_Store
# c.f. http://www.westwind.com/reference/os-x/invisibles.html
.Trashes
# c.f. http://www.westwind.com/reference/os-x/invisibles.html
*.swp
# *.lock - this is used and abused by many editors for many different things.
# For the main ones I use (e.g. Eclipse), it should be excluded
# from source-control, but YMMV
*.lock
#
# profile - REMOVED temporarily (on double-checking, this seems incorrect; I can't find it in OS X docs?)
#profile
####
# Xcode temporary files that should never be committed
#
# NB: NIB/XIB files still exist even on Storyboard projects, so we want this...
*~.nib
####
# Xcode build files -
#
# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData"
DerivedData/
# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build"
build/
#####
# Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups)
#
# This is complicated:
#
# SOMETIMES you need to put this file in version control.
# Apple designed it poorly - if you use "custom executables", they are
# saved in this file.
# 99% of projects do NOT use those, so they do NOT want to version control this file.
# ..but if you're in the 1%, comment out the line "*.pbxuser"
# .pbxuser: http://lists.apple.com/archives/xcode-users/2004/Jan/msg00193.html
*.pbxuser
# .mode1v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html
*.mode1v3
# .mode2v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html
*.mode2v3
# .perspectivev3: http://stackoverflow.com/questions/5223297/xcode-projects-what-is-a-perspectivev3-file
*.perspectivev3
# NB: also, whitelist the default ones, some projects need to use these
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
####
# Xcode 4 - semi-personal settings
#
#
# OPTION 1: ---------------------------------
# throw away ALL personal settings (including custom schemes!
# - unless they are "shared")
#
# NB: this is exclusive with OPTION 2 below
xcuserdata
# OPTION 2: ---------------------------------
# get rid of ALL personal settings, but KEEP SOME OF THEM
# - NB: you must manually uncomment the bits you want to keep
#
# NB: this *requires* git v1.8.2 or above; you may need to upgrade to latest OS X,
# or manually install git over the top of the OS X version
# NB: this is exclusive with OPTION 1 above
#
#xcuserdata/**/*
# (requires option 2 above): Personal Schemes
#
#!xcuserdata/**/xcschemes/*
####
# XCode 4 workspaces - more detailed
#
# Workspaces are important! They are a core feature of Xcode - don't exclude them :)
#
# Workspace layout is quite spammy. For reference:
#
# /(root)/
# /(project-name).xcodeproj/
# project.pbxproj
# /project.xcworkspace/
# contents.xcworkspacedata
# /xcuserdata/
# /(your name)/xcuserdatad/
# UserInterfaceState.xcuserstate
# /xcsshareddata/
# /xcschemes/
# (shared scheme name).xcscheme
# /xcuserdata/
# /(your name)/xcuserdatad/
# (private scheme).xcscheme
# xcschememanagement.plist
#
#
####
# Xcode 4 - Deprecated classes
#
# Allegedly, if you manually "deprecate" your classes, they get moved here.
#
# We're using source-control, so this is a "feature" that we do not want!
*.moved-aside
####
# UNKNOWN: recommended by others, but I can't discover what these files are
#
# ...none. Everything is now explained.
####
# Pods ignoring:
Pods
!Podfile.lock
# xccheckout ignoring
# ref. http://stackoverflow.com/questions/18340453/should-xccheckout-files-in-xcode5-be-ignored-under-vcs
*.xccheckout
# AppCode configuration ignoring
.idea/

48
Classes/Default/RMRPullToRefreshBaseMessageView.swift Normal file → Executable file
View File

@ -33,16 +33,16 @@ class RMRPullToRefreshBaseMessageView: RMRPullToRefreshBaseView {
func configureLabel() {
let label = UILabel(frame: self.messageView.bounds)
label.textColor = UIColor.whiteColor()
label.textAlignment = .Center
label.textColor = UIColor.white
label.textAlignment = .center
label.text = messageText()
messageView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
for attribute in [NSLayoutAttribute.Top, NSLayoutAttribute.Right, NSLayoutAttribute.Left, NSLayoutAttribute.Bottom] {
for attribute in [NSLayoutConstraint.Attribute.top, NSLayoutConstraint.Attribute.right, NSLayoutConstraint.Attribute.left, NSLayoutConstraint.Attribute.bottom] {
messageView.addConstraint(NSLayoutConstraint(item: label,
attribute: attribute,
relatedBy: NSLayoutRelation.Equal,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: messageView,
attribute: attribute,
multiplier: 1,
@ -58,36 +58,36 @@ class RMRPullToRefreshBaseMessageView: RMRPullToRefreshBaseView {
messageView.translatesAutoresizingMaskIntoConstraints = false
let heightConstraint = NSLayoutConstraint(item: messageView,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
attribute: NSLayoutConstraint.Attribute.height,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
attribute: NSLayoutConstraint.Attribute.notAnAttribute,
multiplier: 1,
constant: 30)
let widthConstraint = NSLayoutConstraint(item: messageView,
attribute: NSLayoutAttribute.Width,
relatedBy: NSLayoutRelation.Equal,
attribute: NSLayoutConstraint.Attribute.width,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
attribute: NSLayoutConstraint.Attribute.notAnAttribute,
multiplier: 1,
constant: 150)
messageView.addConstraints([heightConstraint, widthConstraint])
let verticalConstraint = NSLayoutConstraint(item: messageView,
attribute: .CenterY,
relatedBy: NSLayoutRelation.Equal,
attribute: .centerY,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: self,
attribute: .CenterY,
attribute: .centerY,
multiplier: 1,
constant: 0)
let leftConstraint = NSLayoutConstraint(item: messageView,
attribute: .Left,
relatedBy: NSLayoutRelation.Equal,
attribute: .left,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: self,
attribute: .Right,
attribute: .right,
multiplier: 1,
constant: 0)
@ -97,7 +97,7 @@ class RMRPullToRefreshBaseMessageView: RMRPullToRefreshBaseView {
}
func messageBackgroundColor() -> UIColor {
return UIColor.whiteColor()
return UIColor.white
}
func messageText() -> String? {
@ -114,20 +114,20 @@ class RMRPullToRefreshBaseMessageView: RMRPullToRefreshBaseView {
if self.messageViewLeftConstaint?.constant != 0.0 {
self.messageViewLeftConstaint?.constant = 0.0
}
UIView.animateWithDuration(0.4) {[weak self] in
UIView.animate(withDuration: 0.4, animations: {[weak self] in
self?.layoutIfNeeded()
}
})
}
override func willEndLoadingAnimation() {
self.logoHorizontalConstraint?.constant = -CGRectGetWidth(self.bounds)/2.0 + CGRectGetWidth(self.logoImageView.bounds)
self.messageViewLeftConstaint?.constant = -CGRectGetWidth(messageView.bounds) - 10.0
UIView.animateWithDuration(0.4) {[weak self] in
self.logoHorizontalConstraint?.constant = -self.bounds.width/2.0 + self.logoImageView.bounds.width
self.messageViewLeftConstaint?.constant = -messageView.bounds.width - 10.0
UIView.animate(withDuration: 0.4, animations: {[weak self] in
self?.layoutIfNeeded()
}
})
}
override func didEndLoadingAnimation(hidden: Bool) {
override func didEndLoadingAnimation(_ hidden: Bool) {
super.didEndLoadingAnimation(hidden)
if hidden {
self.logoHorizontalConstraint?.constant = 0.0

44
Classes/Default/RMRPullToRefreshBaseView.swift Normal file → Executable file
View File

@ -45,36 +45,36 @@ class RMRPullToRefreshBaseView: RMRPullToRefreshView {
func configureConstraints() {
logoImageView.translatesAutoresizingMaskIntoConstraints = false
let heightConstraint = NSLayoutConstraint(item: logoImageView,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
attribute: NSLayoutConstraint.Attribute.height,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
attribute: NSLayoutConstraint.Attribute.notAnAttribute,
multiplier: 1,
constant: 50)
let widthConstraint = NSLayoutConstraint(item: logoImageView,
attribute: NSLayoutAttribute.Width,
relatedBy: NSLayoutRelation.Equal,
attribute: NSLayoutConstraint.Attribute.width,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
attribute: NSLayoutConstraint.Attribute.notAnAttribute,
multiplier: 1,
constant: 50)
logoImageView.addConstraints([heightConstraint, widthConstraint])
let verticalConstraint = NSLayoutConstraint(item: logoImageView,
attribute: .CenterY,
relatedBy: NSLayoutRelation.Equal,
attribute: .centerY,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: self,
attribute: .CenterY,
attribute: .centerY,
multiplier: 1,
constant: 0)
let horizontalConstraint = NSLayoutConstraint(item: logoImageView,
attribute: .CenterX,
relatedBy: NSLayoutRelation.Equal,
attribute: .centerX,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: self,
attribute: .CenterX,
attribute: .centerX,
multiplier: 1,
constant: 0)
@ -85,18 +85,18 @@ class RMRPullToRefreshBaseView: RMRPullToRefreshView {
func resetTransformIfNecessary() {
if !isConfigured {
logoImageView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
logoImageView.transform = CGAffineTransform(rotationAngle: .pi)
didRotateToBottom = true
isConfigured = true
}
}
func makeIncreasePulling(animated: Bool) {
func makeIncreasePulling(_ animated: Bool) {
didRotateToTop = true
didRotateToBottom = false
let rotateTransform = CGAffineTransformRotate(logoImageView.transform, CGFloat(M_PI));
let rotateTransform = logoImageView.transform.rotated(by: .pi);
if animated {
UIView .animateWithDuration(0.4, animations: { [weak self] in
UIView .animate(withDuration: 0.4, animations: { [weak self] in
self?.logoImageView.transform = rotateTransform
})
} else {
@ -104,12 +104,12 @@ class RMRPullToRefreshBaseView: RMRPullToRefreshView {
}
}
func makeDecreasePulling(animated: Bool) {
func makeDecreasePulling(_ animated: Bool) {
didRotateToBottom = true
didRotateToTop = false
let rotateTransform = CGAffineTransformRotate(logoImageView.transform, -CGFloat(M_PI));
let rotateTransform = logoImageView.transform.rotated(by: -.pi);
if animated {
UIView .animateWithDuration(0.4, animations: { [weak self] in
UIView .animate(withDuration: 0.4, animations: { [weak self] in
self?.logoImageView.transform = rotateTransform
})
} else {
@ -119,7 +119,7 @@ class RMRPullToRefreshBaseView: RMRPullToRefreshView {
// MARK: - RMRPullToRefreshViewProtocol
override func didChangeDraggingProgress(progress: CGFloat) {
override func didChangeDraggingProgress(_ progress: CGFloat) {
resetTransformIfNecessary()
@ -133,7 +133,7 @@ class RMRPullToRefreshBaseView: RMRPullToRefreshView {
}
}
override func prepareForLoadingAnimation(startProgress: CGFloat) {
override func prepareForLoadingAnimation(_ startProgress: CGFloat) {
if logoImageView.animationImages == nil {
logoImageView.animationImages = images
logoImageView.animationDuration = 0.8
@ -145,7 +145,7 @@ class RMRPullToRefreshBaseView: RMRPullToRefreshView {
logoImageView.startAnimating()
}
override func didEndLoadingAnimation(hidden: Bool) {
override func didEndLoadingAnimation(_ hidden: Bool) {
logoImageView.stopAnimating()
logoImageView.layer.removeAllAnimations()
didRotateToTop = false

0
Classes/Default/RMRPullToRefreshErrorView.swift Normal file → Executable file
View File

0
Classes/Default/RMRPullToRefreshNoUpdatesView.swift Normal file → Executable file
View File

0
Classes/Default/RMRPullToRefreshSuccessView.swift Normal file → Executable file
View File

10
Classes/Default/RMRPullToRefreshViewFactory.swift Normal file → Executable file
View File

@ -8,15 +8,15 @@
import UIKit
public class RMRPullToRefreshViewFactory: NSObject {
open class RMRPullToRefreshViewFactory: NSObject {
class func create(result: RMRPullToRefreshResultType) -> RMRPullToRefreshView? {
class func create(_ result: RMRPullToRefreshResultType) -> RMRPullToRefreshView? {
switch result {
case .Success:
case .success:
return RMRPullToRefreshSuccessView(result: result)
case .NoUpdates:
case .noUpdates:
return RMRPullToRefreshNoUpdatesView(result: result)
case .Error:
case .error:
return RMRPullToRefreshErrorView(result: result)
}

View File

@ -8,64 +8,73 @@
import UIKit
public class RMRPullToRefresh: NSObject {
open class RMRPullToRefresh: NSObject {
private var сontroller: RMRPullToRefreshController?
public var height : CGFloat = RMRPullToRefreshConstants.DefaultHeight {
open var height : CGFloat = RMRPullToRefreshConstants.DefaultHeight {
didSet {
сontroller?.configureHeight(height)
}
}
public var backgroundColor : UIColor = RMRPullToRefreshConstants.DefaultBackgroundColor {
open var backgroundColor : UIColor = RMRPullToRefreshConstants.DefaultBackgroundColor {
didSet {
сontroller?.configureBackgroundColor(backgroundColor)
}
}
public var hideWhenError: Bool = true {
open var hideWhenError: Bool = true {
didSet {
сontroller?.hideWhenError = hideWhenError
}
}
public init(scrollView: UIScrollView, position:RMRPullToRefreshPosition, actionHandler: () -> Void) {
public init(
scrollView: UIScrollView,
position:RMRPullToRefreshPosition,
actionHandler: @escaping () -> Void)
{
super.init()
let controller = RMRPullToRefreshController(scrollView: scrollView,
position: position,
actionHandler: actionHandler)
let controller = RMRPullToRefreshController(
scrollView: scrollView,
position: position,
actionHandler: actionHandler)
scrollView.addSubview(controller.containerView)
self.сontroller = controller
}
public func configureView(view :RMRPullToRefreshView, state:RMRPullToRefreshState, result:RMRPullToRefreshResultType) {
open func configureView(_ view :RMRPullToRefreshView, state:RMRPullToRefreshState, result:RMRPullToRefreshResultType) {
сontroller?.configureView(view, state: state, result: result)
}
public func configureView(view :RMRPullToRefreshView, result:RMRPullToRefreshResultType) {
open func configureView(_ view :RMRPullToRefreshView, result:RMRPullToRefreshResultType) {
сontroller?.configureView(view, result: result)
}
public func setupDefaultSettings() {
open func setupDefaultSettings() {
сontroller?.setupDefaultSettings()
}
public func startLoading() {
open func startLoading() {
сontroller?.startLoading()
}
public func stopLoading() {
stopLoading(.Success)
open func stopLoading() {
stopLoading(.success)
}
public func stopLoading(result:RMRPullToRefreshResultType) {
open func stopLoading(_ result:RMRPullToRefreshResultType) {
сontroller?.stopLoading(result)
}
public func setHideDelay(delay: NSTimeInterval, result: RMRPullToRefreshResultType) {
open func setHideDelay(_ delay: TimeInterval, result: RMRPullToRefreshResultType) {
сontroller?.setHideDelay(delay, result: result)
}
open var state: RMRPullToRefreshState {
return сontroller?.state ?? .stopped
}
}

28
Classes/RMRPullToRefreshConstants.swift Normal file → Executable file
View File

@ -9,32 +9,24 @@
import UIKit
public enum RMRPullToRefreshPosition: Int {
case Top
case Bottom
case top
case bottom
}
public enum RMRPullToRefreshState: Int {
case Stopped
case Dragging
case Loading
case stopped
case dragging
case loading
}
public enum RMRPullToRefreshResultType: Int {
case Success = 0
case NoUpdates
case Error
case success = 0
case noUpdates
case error
}
public struct RMRPullToRefreshConstants {
struct KeyPaths {
static let ContentOffset = "contentOffset"
static let ContentSize = "contentSize"
static let ContentInset = "contentInset"
static let PanState = "pan.state"
static let Frame = "frame"
}
static let DefaultHeight = CGFloat(90.0)
static let DefaultBackgroundColor = UIColor.whiteColor()
}
static let DefaultBackgroundColor = UIColor.white
}

28
Classes/RMRPullToRefreshContainerView.swift Normal file → Executable file
View File

@ -8,39 +8,39 @@
import UIKit
public class RMRPullToRefreshContainerView: UIView {
open class RMRPullToRefreshContainerView: UIView {
var currentView: RMRPullToRefreshView?
var storage = [String: RMRPullToRefreshView]()
public func configureView(view:RMRPullToRefreshView, state:RMRPullToRefreshState, result:RMRPullToRefreshResultType) {
open func configureView(_ view:RMRPullToRefreshView, state:RMRPullToRefreshState, result:RMRPullToRefreshResultType) {
let key = storageKey(state, result:result)
self.storage[key] = view
}
func updateView(state: RMRPullToRefreshState, result: RMRPullToRefreshResultType) {
func updateView(_ state: RMRPullToRefreshState, result: RMRPullToRefreshResultType) {
clear()
if let view = obtainView(state, result: result) {
view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view)
addConstraint(constraint(self, subview: view, attribute: NSLayoutAttribute.Left))
addConstraint(constraint(self, subview: view, attribute: NSLayoutAttribute.Top))
addConstraint(constraint(self, subview: view, attribute: NSLayoutAttribute.Right))
addConstraint(constraint(self, subview: view, attribute: NSLayoutAttribute.Bottom))
addConstraint(constraint(self, subview: view, attribute: NSLayoutConstraint.Attribute.left))
addConstraint(constraint(self, subview: view, attribute: NSLayoutConstraint.Attribute.top))
addConstraint(constraint(self, subview: view, attribute: NSLayoutConstraint.Attribute.right))
addConstraint(constraint(self, subview: view, attribute: NSLayoutConstraint.Attribute.bottom))
view.layoutIfNeeded()
self.currentView = view
}
}
func dragging(progress: CGFloat) {
func dragging(_ progress: CGFloat) {
if let view = self.currentView {
view.didChangeDraggingProgress(progress)
}
}
func startLoadingAnimation(startProgress: CGFloat) {
func startLoadingAnimation(_ startProgress: CGFloat) {
if let view = self.currentView {
if !view.pullToRefreshIsLoading {
view.prepareForLoadingAnimation(startProgress)
@ -56,7 +56,7 @@ public class RMRPullToRefreshContainerView: UIView {
}
}
func stopAllAnimations(hidden: Bool) {
func stopAllAnimations(_ hidden: Bool) {
for view in storage.values {
view.didEndLoadingAnimation(hidden)
view.pullToRefreshIsLoading = false
@ -72,19 +72,19 @@ public class RMRPullToRefreshContainerView: UIView {
self.currentView = nil
}
func obtainView(state: RMRPullToRefreshState, result: RMRPullToRefreshResultType) -> RMRPullToRefreshView? {
func obtainView(_ state: RMRPullToRefreshState, result: RMRPullToRefreshResultType) -> RMRPullToRefreshView? {
let key = storageKey(state, result:result)
return self.storage[key]
}
func storageKey(state: RMRPullToRefreshState, result: RMRPullToRefreshResultType) -> String {
func storageKey(_ state: RMRPullToRefreshState, result: RMRPullToRefreshResultType) -> String {
return String(state.rawValue) + "_" + String(result.rawValue)
}
// MARK: - Constraint
func constraint(superview: UIView, subview: UIView, attribute: NSLayoutAttribute) -> NSLayoutConstraint {
return NSLayoutConstraint(item: subview, attribute: attribute, relatedBy: NSLayoutRelation.Equal, toItem: superview, attribute: attribute, multiplier: 1, constant: 0)
func constraint(_ superview: UIView, subview: UIView, attribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint {
return NSLayoutConstraint(item: subview, attribute: attribute, relatedBy: NSLayoutConstraint.Relation.equal, toItem: superview, attribute: attribute, multiplier: 1, constant: 0)
}
}

View File

@ -8,7 +8,7 @@
import UIKit
public class RMRPullToRefreshController: NSObject {
open class RMRPullToRefreshController {
// MARK: - Vars
@ -16,12 +16,11 @@ public class RMRPullToRefreshController: NSObject {
let containerView = RMRPullToRefreshContainerView()
let backgroundView = UIView(frame: CGRectZero)
let backgroundView = UIView(frame: CGRect.zero)
var backgroundViewHeightConstraint: NSLayoutConstraint?
var backgroundViewTopConstraint: NSLayoutConstraint?
var stopped = true
var subscribing = false
var actionHandler: (() -> Void)!
@ -29,22 +28,27 @@ public class RMRPullToRefreshController: NSObject {
var originalTopInset = CGFloat(0.0)
var originalBottomInset = CGFloat(0.0)
var state = RMRPullToRefreshState.Stopped
var result = RMRPullToRefreshResultType.Success
var state = RMRPullToRefreshState.stopped
var result = RMRPullToRefreshResultType.success
var position: RMRPullToRefreshPosition?
var changingContentInset = false
var contentSizeWhenStartLoading: CGSize?
var hideDelayValues = [RMRPullToRefreshResultType: NSTimeInterval]()
var hideDelayValues = [RMRPullToRefreshResultType: TimeInterval]()
public var hideWhenError: Bool = true
open var hideWhenError: Bool = true
// MARK: - Observation
private var contentOffsetObservation: NSKeyValueObservation?
private var contentSizeObservation: NSKeyValueObservation?
private var panStateObservation: NSKeyValueObservation?
// MARK: - Init
init(scrollView: UIScrollView, position:RMRPullToRefreshPosition, actionHandler: () -> Void) {
super.init()
init(scrollView: UIScrollView, position:RMRPullToRefreshPosition, actionHandler: @escaping () -> Void) {
self.scrollView = scrollView
self.actionHandler = actionHandler
self.position = position
@ -52,39 +56,41 @@ public class RMRPullToRefreshController: NSObject {
self.configureBackgroundView(self.backgroundView)
self.configureHeight()
self.containerView.backgroundColor = UIColor.clearColor()
self.containerView.backgroundColor = UIColor.clear
self.subscribeOnScrollViewEvents()
}
deinit {
self.unsubscribeFromScrollViewEvents()
}
private func configureBackgroundView(backgroundView: UIView) {
private func configureBackgroundView(_ backgroundView: UIView) {
backgroundView.translatesAutoresizingMaskIntoConstraints = false
scrollView?.addSubview(backgroundView)
addBackgroundViewConstraints(backgroundView)
}
private func addBackgroundViewConstraints(backgroundView: UIView) {
// Constraints
self.backgroundViewHeightConstraint = NSLayoutConstraint(item: backgroundView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 0)
backgroundView.addConstraint(self.backgroundViewHeightConstraint!)
private func addBackgroundViewConstraints(_ backgroundView: UIView) {
guard let scrollView = scrollView, let position = position else {
return
}
let backgroundViewHeightConstraint = backgroundView.heightAnchor.constraint(equalToConstant: 0)
backgroundViewHeightConstraint.isActive = true
self.backgroundViewHeightConstraint = backgroundViewHeightConstraint
scrollView?.addConstraint(NSLayoutConstraint(item: backgroundView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Width, multiplier: 1, constant: 0))
backgroundView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
if position == .Top {
scrollView?.addConstraint(NSLayoutConstraint(item: backgroundView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0))
} else if position == .Bottom, let scrollView = self.scrollView {
let constant = max(scrollView.contentSize.height, CGRectGetHeight(scrollView.bounds))
self.backgroundViewTopConstraint = NSLayoutConstraint(item: backgroundView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: constant)
scrollView.addConstraint(self.backgroundViewTopConstraint!)
switch position {
case .top:
backgroundView.bottomAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
case .bottom:
let constant = max(scrollView.contentSize.height, scrollView.bounds.height)
let backgroundViewTopConstraint = backgroundView.topAnchor.constraint(
equalTo: scrollView.bottomAnchor, constant: constant)
backgroundViewTopConstraint.isActive = true
self.backgroundViewTopConstraint = backgroundViewTopConstraint
}
}
private func configureHeight() {
if let scrollView = self.scrollView {
self.originalTopInset = scrollView.contentInset.top
self.originalBottomInset = scrollView.contentInset.bottom
@ -94,96 +100,101 @@ public class RMRPullToRefreshController: NSObject {
// MARK: - Public
public func configureView(view:RMRPullToRefreshView, result:RMRPullToRefreshResultType) {
configureView(view, state: .Loading, result: result)
configureView(view, state: .Dragging, result: result)
configureView(view, state: .Stopped, result: result)
open func configureView(_ view:RMRPullToRefreshView, result:RMRPullToRefreshResultType) {
configureView(view, state: .loading, result: result)
configureView(view, state: .dragging, result: result)
configureView(view, state: .stopped, result: result)
}
public func configureView(view:RMRPullToRefreshView, state:RMRPullToRefreshState, result:RMRPullToRefreshResultType) {
open func configureView(_ view:RMRPullToRefreshView, state:RMRPullToRefreshState, result:RMRPullToRefreshResultType) {
containerView.configureView(view, state: state, result: result)
}
public func configureHeight(height: CGFloat) {
open func configureHeight(_ height: CGFloat) {
self.height = height
updateContainerFrame()
}
public func configureBackgroundColor(color: UIColor) {
open func configureBackgroundColor(_ color: UIColor) {
self.backgroundView.backgroundColor = color
}
public func setupDefaultSettings() {
setupDefaultSettings(.Success, hideDelay: 0.0)
setupDefaultSettings(.NoUpdates, hideDelay: 2.0)
setupDefaultSettings(.Error, hideDelay: 2.0)
configureBackgroundColor(UIColor.whiteColor())
open func setupDefaultSettings() {
setupDefaultSettings(.success, hideDelay: 0.0)
setupDefaultSettings(.noUpdates, hideDelay: 2.0)
setupDefaultSettings(.error, hideDelay: 2.0)
configureBackgroundColor(UIColor.white)
updateContainerView(self.state)
}
public func startLoading() {
open func startLoading() {
startLoading(0.0)
}
public func stopLoading(result:RMRPullToRefreshResultType) {
open func stopLoading(_ result:RMRPullToRefreshResultType) {
self.result = result
self.state = .Stopped
self.state = .stopped
updateContainerView(self.state)
containerView.prepareForStopAnimations()
var delay = hideDelay(result)
var afterDelay = 0.4
let afterDelay = 0.4
if result == .Error && !hideWhenError {
if result == .error && !hideWhenError {
delay = 0.0
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), { [weak self] in
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: { [weak self] in
if self?.shouldHideWhenStopLoading() == true {
self?.resetContentInset()
if let position = self?.position {
switch (position) {
case .Top:
case .top:
self?.scrollToTop(true)
case .Bottom:
case .bottom:
self?.scrollToBottom(true)
}
}
self?.contentSizeWhenStartLoading = nil
self?.performSelector(#selector(self?.resetBackgroundViewHeightConstraint), withObject: nil, afterDelay: afterDelay)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + afterDelay) {
self?.resetBackgroundViewHeightConstraint()
}
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + afterDelay) {
self?.stopAllAnimations()
}
self?.performSelector(#selector(self?.stopAllAnimations), withObject: nil, afterDelay: afterDelay)
})
}
public func setHideDelay(delay: NSTimeInterval, result: RMRPullToRefreshResultType) {
open func setHideDelay(_ delay: TimeInterval, result: RMRPullToRefreshResultType) {
self.hideDelayValues[result] = delay
}
// MARK: - Private
func setupDefaultSettings(result:RMRPullToRefreshResultType, hideDelay: NSTimeInterval) {
func setupDefaultSettings(_ result:RMRPullToRefreshResultType, hideDelay: TimeInterval) {
if let view = RMRPullToRefreshViewFactory.create(result) {
configureView(view, result: result)
setHideDelay(hideDelay, result: result)
}
}
func scrollToTop(animated: Bool) {
func scrollToTop(_ animated: Bool) {
if let scrollView = self.scrollView {
if scrollView.contentOffset.y < -originalTopInset {
let offset = CGPointMake(scrollView.contentOffset.x, -self.originalTopInset)
let offset = CGPoint(x: scrollView.contentOffset.x, y: -self.originalTopInset)
scrollView.setContentOffset(offset, animated: true)
}
}
}
func scrollToBottom(animated: Bool) {
func scrollToBottom(_ animated: Bool) {
if let scrollView = self.scrollView {
var offset = scrollView.contentOffset
if let contentSize = self.contentSizeWhenStartLoading {
offset.y = contentSize.height - CGRectGetHeight(scrollView.bounds) + scrollView.contentInset.bottom
if state == .Stopped {
offset.y = contentSize.height - scrollView.bounds.height + scrollView.contentInset.bottom
if state == .stopped {
if scrollView.contentOffset.y < offset.y {
return
} else if scrollView.contentOffset.y > offset.y {
@ -191,16 +202,16 @@ public class RMRPullToRefreshController: NSObject {
}
}
} else {
offset.y = scrollView.contentSize.height - CGRectGetHeight(scrollView.bounds) + scrollView.contentInset.bottom
offset.y = scrollView.contentSize.height - scrollView.bounds.height + scrollView.contentInset.bottom
}
scrollView.setContentOffset(offset, animated: animated)
}
}
func startLoading(startProgress: CGFloat) {
func startLoading(_ startProgress: CGFloat) {
stopped = false
contentSizeWhenStartLoading = scrollView?.contentSize
state = .Loading
state = .loading
updateContainerView(state)
actionHandler()
@ -223,95 +234,106 @@ public class RMRPullToRefreshController: NSObject {
backgroundViewHeightConstraint?.constant = 0
}
private func scrollViewDidChangePanState(scrollView: UIScrollView, panState: UIGestureRecognizerState) {
if panState == .Ended || panState == .Cancelled || panState == .Failed {
private func scrollViewDidChangePanState(_ scrollView: UIScrollView, panState: UIGestureRecognizer.State) {
if panState == .ended || panState == .cancelled || panState == .failed {
if state == .Loading || (shouldHideWhenStopLoading() && !stopped) {
if state == .loading || (shouldHideWhenStopLoading() && !stopped) {
return
}
var y: CGFloat = 0.0
if position == .Top {
if position == .top {
y = -scrollView.contentOffset.y
} else if position == .Bottom {
y = -(scrollView.contentSize.height - (scrollView.contentOffset.y + CGRectGetHeight(scrollView.bounds) + originalBottomInset));
} else if position == .bottom {
y = -(scrollView.contentSize.height - (scrollView.contentOffset.y + scrollView.bounds.height + originalBottomInset));
}
if y >= height {
startLoading(y/height)
// inset
var inset = scrollView.contentInset
if position == .Top {
if position == .top {
inset.top = originalTopInset+height
} else if position == .Bottom {
} else if position == .bottom {
inset.bottom = originalBottomInset+height
}
setContentInset(inset, animated: true)
} else {
state = .Stopped
state = .stopped
updateContainerView(state)
}
}
}
private func scrollViewDidChangeContentSize(scrollView: UIScrollView, contentSize: CGSize) {
private func scrollViewDidChangeContentSize(_ scrollView: UIScrollView, contentSize: CGSize) {
updateContainerFrame()
if position == .Bottom {
self.backgroundViewTopConstraint?.constant = max(scrollView.contentSize.height, CGRectGetHeight(scrollView.bounds))
if position == .bottom {
self.backgroundViewTopConstraint?.constant = max(scrollView.contentSize.height, scrollView.bounds.height)
if changingContentInset {
scrollToBottom(true)
}
}
}
private func scrollViewDidScroll(scrollView: UIScrollView, contentOffset: CGPoint) {
private func scrollViewDidScroll(_ scrollView: UIScrollView, contentOffset: CGPoint) {
if state == .loading {
if scrollView.contentOffset.y >= 0 {
scrollView.contentInset = UIEdgeInsets.zero
} else {
scrollView.contentInset = UIEdgeInsets.init(top: min(-scrollView.contentOffset.y, originalTopInset+height),left: 0,bottom: 0,right: 0)
}
}
if !stopped {
return
}
if scrollView.dragging && state == .Stopped {
state = .Dragging
if scrollView.isDragging && state == .stopped {
state = .dragging
updateContainerView(state)
}
var y: CGFloat = 0.0
if position == .Top {
if position == .top {
y = -(contentOffset.y)
} else if position == .Bottom {
y = -(scrollView.contentSize.height - (contentOffset.y + CGRectGetHeight(scrollView.bounds) + originalBottomInset))
} else if position == .bottom {
y = -(scrollView.contentSize.height - (contentOffset.y + scrollView.bounds.height + originalBottomInset))
}
if y > 0 {
if state == .Dragging {
if state == .dragging {
containerView.dragging(y/height)
}
configureBackgroundHeightConstraint(y, contentInset: scrollView.contentInset)
}
}
private func configureBackgroundHeightConstraint(contentOffsetY: CGFloat, contentInset: UIEdgeInsets) {
private func configureBackgroundHeightConstraint(_ contentOffsetY: CGFloat, contentInset: UIEdgeInsets) {
var constant = CGFloat(-1.0)
if position == .Top {
if position == .top {
constant = contentOffsetY + contentInset.top
} else {
constant = contentOffsetY + contentInset.bottom
}
if constant > 0 && constant > backgroundViewHeightConstraint?.constant {
backgroundViewHeightConstraint?.constant = constant
if let backgroundViewHeightConstraint = backgroundViewHeightConstraint,
constant > 0,
constant > backgroundViewHeightConstraint.constant {
backgroundViewHeightConstraint.constant = constant
}
}
func updateContainerView(state: RMRPullToRefreshState) {
func updateContainerView(_ state: RMRPullToRefreshState) {
containerView.updateView(state, result: self.result)
}
func updateContainerFrame() {
if let scrollView = self.scrollView, let position = self.position {
var frame = CGRectZero
var frame = CGRect.zero
switch (position) {
case .Top:
frame = CGRectMake(0, -height, CGRectGetWidth(scrollView.bounds), height)
case .Bottom:
let y = max(scrollView.contentSize.height, CGRectGetHeight(scrollView.bounds))
frame = CGRectMake(0, y, CGRectGetWidth(scrollView.bounds), height)
case .top:
frame = CGRect(x: 0, y: -height, width: scrollView.bounds.width, height: height)
case .bottom:
let y = max(scrollView.contentSize.height, scrollView.bounds.height)
frame = CGRect(x: 0, y: y, width: scrollView.bounds.width, height: height)
}
self.containerView.frame = frame
@ -322,20 +344,20 @@ public class RMRPullToRefreshController: NSObject {
if let scrollView = scrollView, let position = self.position {
var inset = scrollView.contentInset
switch (position) {
case .Top:
case .top:
inset.top = originalTopInset
case .Bottom:
case .bottom:
inset.bottom = originalBottomInset
}
setContentInset(inset, animated: true)
}
}
func setContentInset(contentInset: UIEdgeInsets, animated: Bool) {
func setContentInset(_ contentInset: UIEdgeInsets, animated: Bool) {
changingContentInset = true
UIView.animateWithDuration(0.3,
UIView.animate(withDuration: 0.3,
delay: 0.0,
options: UIViewAnimationOptions.BeginFromCurrentState,
options: UIView.AnimationOptions.beginFromCurrentState,
animations: { [weak self]() -> Void in
self?.scrollView?.contentInset = contentInset
}, completion: { [weak self](finished) -> Void in
@ -343,20 +365,20 @@ public class RMRPullToRefreshController: NSObject {
})
}
func checkContentSize(scrollView: UIScrollView) -> Bool{
let height = CGRectGetHeight(scrollView.bounds)
func checkContentSize(_ scrollView: UIScrollView) -> Bool{
let height = scrollView.bounds.height
if scrollView.contentSize.height < height {
scrollView.contentSize = CGSizeMake(scrollView.contentSize.width, height)
scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: height)
return false
}
return true
}
func shouldHideWhenStopLoading() -> Bool{
return (result != .Error) || (result == .Error && hideWhenError)
return (result != .error) || (result == .error && hideWhenError)
}
func hideDelay(result: RMRPullToRefreshResultType) -> NSTimeInterval {
func hideDelay(_ result: RMRPullToRefreshResultType) -> TimeInterval {
if let delay = hideDelayValues[result] {
return delay
}
@ -365,42 +387,35 @@ public class RMRPullToRefreshController: NSObject {
// MARK: - KVO
public func subscribeOnScrollViewEvents() {
if !subscribing, let scrollView = self.scrollView {
scrollView.addObserver(self, forKeyPath: RMRPullToRefreshConstants.KeyPaths.ContentOffset, options: .New, context: nil)
scrollView.addObserver(self, forKeyPath: RMRPullToRefreshConstants.KeyPaths.ContentSize, options: .New, context: nil)
scrollView.addObserver(self, forKeyPath: RMRPullToRefreshConstants.KeyPaths.PanState, options: .New, context: nil)
subscribing = true
open func subscribeOnScrollViewEvents() {
guard let scrollView = scrollView else {
return
}
self.contentOffsetObservation = scrollView.observe(
\.contentOffset,
options: [.new]) { [weak self] (scrollView, change) in
guard let newContentOffset = change.newValue else { return }
self?.scrollViewDidScroll(scrollView, contentOffset: newContentOffset)
}
self.contentSizeObservation = scrollView.observe(
\.contentSize,
options: [.new]) { [weak self] (scrollView, change) in
guard let newContentSize = change.newValue else { return }
self?.scrollViewDidChangeContentSize(scrollView, contentSize: newContentSize)
}
self.panStateObservation = scrollView.panGestureRecognizer.observe(
\.state,
options: [.new]) { [weak self] panGestureRecognizer, _ in
self?.scrollViewDidChangePanState(scrollView, panState: panGestureRecognizer.state)
}
}
public func unsubscribeFromScrollViewEvents() {
if subscribing, let scrollView = self.containerView.superview {
scrollView.removeObserver(self, forKeyPath: RMRPullToRefreshConstants.KeyPaths.ContentOffset)
scrollView.removeObserver(self, forKeyPath: RMRPullToRefreshConstants.KeyPaths.ContentSize)
scrollView.removeObserver(self, forKeyPath: RMRPullToRefreshConstants.KeyPaths.PanState)
subscribing = false
}
open func unsubscribeFromScrollViewEvents() {
contentOffsetObservation?.invalidate()
contentSizeObservation?.invalidate()
panStateObservation?.invalidate()
}
override public func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if keyPath == RMRPullToRefreshConstants.KeyPaths.ContentOffset {
if let newContentOffset = change?[NSKeyValueChangeNewKey]?.CGPointValue, scrollView = self.scrollView {
scrollViewDidScroll(scrollView, contentOffset:newContentOffset)
}
} else if keyPath == RMRPullToRefreshConstants.KeyPaths.ContentSize {
if let newContentSize = change?[NSKeyValueChangeNewKey]?.CGSizeValue(), scrollView = self.scrollView {
if checkContentSize(scrollView) {
scrollViewDidChangeContentSize(scrollView, contentSize: newContentSize)
}
}
} else if keyPath == RMRPullToRefreshConstants.KeyPaths.PanState {
if let rawValue = change?[NSKeyValueChangeNewKey] as? Int {
if let state = UIGestureRecognizerState(rawValue: rawValue), scrollView = self.scrollView {
scrollViewDidChangePanState(scrollView, panState: state)
}
}
}
}
}
}

12
Classes/RMRPullToRefreshView.swift Normal file → Executable file
View File

@ -8,18 +8,18 @@
import UIKit
public class RMRPullToRefreshView: UIView, RMRPullToRefreshViewProtocol {
open class RMRPullToRefreshView: UIView, RMRPullToRefreshViewProtocol {
var pullToRefreshIsLoading = false
// Begin Loading
public func prepareForLoadingAnimation(startProgress: CGFloat) {}
public func beginLoadingAnimation() {}
open func prepareForLoadingAnimation(_ startProgress: CGFloat) {}
open func beginLoadingAnimation() {}
// End Loading
public func willEndLoadingAnimation() {}
public func didEndLoadingAnimation(hidden: Bool) {}
open func willEndLoadingAnimation() {}
open func didEndLoadingAnimation(_ hidden: Bool) {}
// Dragging
public func didChangeDraggingProgress(progress: CGFloat) {}
open func didChangeDraggingProgress(_ progress: CGFloat) {}
}

8
Classes/RMRPullToRefreshViewProtocol.swift Normal file → Executable file
View File

@ -11,13 +11,13 @@ import UIKit
public protocol RMRPullToRefreshViewProtocol {
// Begin Loading
func prepareForLoadingAnimation(startProgress: CGFloat)
func prepareForLoadingAnimation(_ startProgress: CGFloat)
func beginLoadingAnimation()
// End Loading
func willEndLoadingAnimation()
func didEndLoadingAnimation(hidden: Bool)
func didEndLoadingAnimation(_ hidden: Bool)
// Dragging
func didChangeDraggingProgress(progress: CGFloat)
}
func didChangeDraggingProgress(_ progress: CGFloat)
}

View File

@ -1,5 +1,11 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!
pod 'RMRPullToRefresh', :git => "git@git.redmadrobot.com:im/RMRPullToRefresh.git"
project 'RMRPullToRefreshExample.xcodeproj'
workspace 'RMRPullToRefreshExample.xcworkspace'
target :RMRPullToRefreshExample do
pod 'RMRPullToRefresh', :path => "../"
end

16
Example/Podfile.lock Normal file
View File

@ -0,0 +1,16 @@
PODS:
- RMRPullToRefresh (0.5.0)
DEPENDENCIES:
- RMRPullToRefresh (from `../`)
EXTERNAL SOURCES:
RMRPullToRefresh:
:path: ../
SPEC CHECKSUMS:
RMRPullToRefresh: 6c25f48af80d0e5d72b89ef5d6ea0dfcc21e5444
PODFILE CHECKSUM: 6bf08c33e827c034420f4dcfc61024a8ba7eab2f
COCOAPODS: 1.3.1

View File

@ -14,30 +14,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(application: UIApplication) {
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

View File

@ -1,5 +1,15 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
@ -29,6 +39,11 @@
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {

View File

@ -1,8 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="9wl-A7-LQ4">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="9wl-A7-LQ4">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Navigation Controller-->
@ -10,9 +14,9 @@
<objects>
<navigationController id="9wl-A7-LQ4" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="yyl-ZQ-eby">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<color key="tintColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="tintColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</navigationBar>
<connections>
<segue destination="oE2-vv-go7" kind="relationship" relationship="rootViewController" id="7GL-Am-OfG"/>
@ -20,79 +24,79 @@
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="jEm-VL-aI5" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-363" y="324"/>
<point key="canvasLocation" x="-503" y="323"/>
</scene>
<!--Table View Controller-->
<scene sceneID="xmD-iM-370">
<objects>
<tableViewController id="oE2-vv-go7" customClass="TableViewController" customModule="RMRPullToRefreshExample" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="C22-ld-kpl">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<sections>
<tableViewSection headerTitle="Perekrestok" id="OAd-tH-sE5">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="Qw1-Fc-VoV" detailTextLabel="nOn-mL-Lrw" imageView="6OD-og-tgv" style="IBUITableViewCellStyleValue1" id="Ig2-F0-5fB">
<rect key="frame" x="0.0" y="113.5" width="600" height="44"/>
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Ig2-F0-5fB" id="f8z-M3-Z4V">
<rect key="frame" x="0.0" y="0.0" width="567" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Perekrestok" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Qw1-Fc-VoV">
<rect key="frame" x="74" y="12" width="86.5" height="19.5"/>
<rect key="frame" x="75" y="12" width="86.5" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Top" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="nOn-mL-Lrw">
<rect key="frame" x="537.5" y="12" width="27.5" height="19.5"/>
<rect key="frame" x="313" y="12" width="27" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="perekrestok" id="6OD-og-tgv">
<rect key="frame" x="15" y="0.0" width="44" height="43.5"/>
<rect key="frame" x="16" y="0.0" width="44" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</imageView>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<segue destination="BYZ-38-t0r" kind="show" identifier="perekrestok_top" id="TFF-yS-c1s"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="3b8-Kd-0Uw" detailTextLabel="U1K-Un-yBw" imageView="a26-Gq-xeL" style="IBUITableViewCellStyleValue1" id="EN2-bQ-yTK">
<rect key="frame" x="0.0" y="157.5" width="600" height="44"/>
<rect key="frame" x="0.0" y="99.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="EN2-bQ-yTK" id="YBm-bQ-aP7">
<rect key="frame" x="0.0" y="0.0" width="567" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Perekrestok" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="3b8-Kd-0Uw">
<rect key="frame" x="74" y="12" width="86.5" height="19.5"/>
<rect key="frame" x="75" y="12" width="86.5" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Bottom" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="U1K-Un-yBw">
<rect key="frame" x="512" y="12" width="53" height="19.5"/>
<rect key="frame" x="287" y="12" width="53" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="perekrestok" id="a26-Gq-xeL">
<rect key="frame" x="15" y="0.0" width="44" height="43.5"/>
<rect key="frame" x="16" y="0.0" width="44" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</imageView>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<segue destination="BYZ-38-t0r" kind="show" identifier="perekrestok_bottom" id="RNW-CJ-jxH"/>
</connections>
@ -102,28 +106,28 @@
<tableViewSection headerTitle="Beeline" id="Bp3-dz-9Zb">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="HlT-ZI-pLV" detailTextLabel="Sne-Dt-pmd" imageView="IEL-Td-m6Q" style="IBUITableViewCellStyleValue1" id="t3C-cF-xlh">
<rect key="frame" x="0.0" y="251.5" width="600" height="44"/>
<rect key="frame" x="0.0" y="199.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="t3C-cF-xlh" id="5E2-Ik-tgT">
<rect key="frame" x="0.0" y="0.0" width="567" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Beeline" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="HlT-ZI-pLV">
<rect key="frame" x="74" y="12" width="53.5" height="19.5"/>
<rect key="frame" x="75" y="12" width="53.5" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Top" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Sne-Dt-pmd">
<rect key="frame" x="537.5" y="12" width="27.5" height="19.5"/>
<rect key="frame" x="313" y="12" width="27" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="beeline" id="IEL-Td-m6Q">
<rect key="frame" x="15" y="0.0" width="44" height="43.5"/>
<rect key="frame" x="16" y="0.0" width="44" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</imageView>
</subviews>
@ -133,28 +137,28 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="uHd-tI-cbh" detailTextLabel="Nfu-Qc-1Gp" imageView="NLI-8k-jWy" style="IBUITableViewCellStyleValue1" id="Qxr-CU-ozv">
<rect key="frame" x="0.0" y="295.5" width="600" height="44"/>
<rect key="frame" x="0.0" y="243.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Qxr-CU-ozv" id="LDM-NR-x0D">
<rect key="frame" x="0.0" y="0.0" width="567" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Beeline" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="uHd-tI-cbh">
<rect key="frame" x="74" y="12" width="53.5" height="19.5"/>
<rect key="frame" x="75" y="12" width="53.5" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Bottom" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Nfu-Qc-1Gp">
<rect key="frame" x="512" y="12" width="53" height="19.5"/>
<rect key="frame" x="287" y="12" width="53" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="beeline" id="NLI-8k-jWy">
<rect key="frame" x="15" y="0.0" width="44" height="43.5"/>
<rect key="frame" x="16" y="0.0" width="44" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</imageView>
</subviews>
@ -168,28 +172,28 @@
<tableViewSection headerTitle="Default" id="YNv-qE-Ndq">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="Hod-kU-jvC" detailTextLabel="MHK-Eb-EhW" imageView="LAe-1f-zZu" style="IBUITableViewCellStyleValue1" id="uGe-5R-mhz">
<rect key="frame" x="0.0" y="389.5" width="600" height="44"/>
<rect key="frame" x="0.0" y="343.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="uGe-5R-mhz" id="h7j-2o-NcK">
<rect key="frame" x="0.0" y="0.0" width="567" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Redmadrobot" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Hod-kU-jvC">
<rect key="frame" x="74" y="12" width="99" height="19.5"/>
<rect key="frame" x="75" y="12" width="99" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Top" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="MHK-Eb-EhW">
<rect key="frame" x="537.5" y="12" width="27.5" height="19.5"/>
<rect key="frame" x="313" y="12" width="27" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="redmadlogo-1" id="LAe-1f-zZu">
<rect key="frame" x="15" y="0.0" width="44" height="43.5"/>
<rect key="frame" x="16" y="0.0" width="44" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</imageView>
</subviews>
@ -199,28 +203,28 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="PEJ-1c-whb" detailTextLabel="cmt-br-dNG" imageView="1iM-wK-6Uf" style="IBUITableViewCellStyleValue1" id="Mlu-1N-6jM">
<rect key="frame" x="0.0" y="433.5" width="600" height="44"/>
<rect key="frame" x="0.0" y="387.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Mlu-1N-6jM" id="Xfk-9c-f5n">
<rect key="frame" x="0.0" y="0.0" width="567" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Redmadrobot" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="PEJ-1c-whb">
<rect key="frame" x="74" y="12" width="99" height="19.5"/>
<rect key="frame" x="75" y="12" width="99" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Bottom" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="cmt-br-dNG">
<rect key="frame" x="512" y="12" width="53" height="19.5"/>
<rect key="frame" x="287" y="12" width="53" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="redmadlogo-1" id="1iM-wK-6Uf">
<rect key="frame" x="15" y="0.0" width="44" height="43.5"/>
<rect key="frame" x="16" y="0.0" width="44" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</imageView>
</subviews>
@ -241,7 +245,7 @@
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="2e9-Jk-9A8" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="269" y="324"/>
<point key="canvasLocation" x="143" y="323"/>
</scene>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
@ -252,26 +256,26 @@
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="qa0-J0-SDd">
<rect key="frame" x="0.0" y="64" width="600" height="536"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="separatorColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="separatorColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="pxY-0R-2l7" style="IBUITableViewCellStyleDefault" id="RzC-n2-Iry">
<rect key="frame" x="0.0" y="28" width="600" height="44"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="pxY-0R-2l7" style="IBUITableViewCellStyleDefault" id="RzC-n2-Iry">
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="RzC-n2-Iry" id="QD1-aM-jfC">
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="pxY-0R-2l7">
<rect key="frame" x="15" y="0.0" width="570" height="44"/>
<rect key="frame" x="15" y="0.0" width="345" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
@ -284,7 +288,7 @@
</connections>
</tableView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="qa0-J0-SDd" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" constant="64" id="6Du-Fj-JAv"/>
<constraint firstItem="qa0-J0-SDd" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="ONC-Bk-CbX"/>
@ -311,7 +315,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1011" y="324"/>
<point key="canvasLocation" x="1111" y="323"/>
</scene>
</scenes>
<resources>

View File

@ -10,14 +10,14 @@ import UIKit
import RMRPullToRefresh
enum AnimationStage: Int {
case Stage1 // big medium small
case Stage2 // big medium
case Stage3 // big
case Stage4 //
case Stage5 // big
case Stage6 // big medium
case stage1 // big medium small
case stage2 // big medium
case stage3 // big
case stage4 //
case stage5 // big
case stage6 // big medium
static var count: Int { return AnimationStage.Stage6.hashValue + 1}
static var count: Int { return AnimationStage.stage6.hashValue + 1}
}
class BeelineView: RMRPullToRefreshView {
@ -30,22 +30,22 @@ class BeelineView: RMRPullToRefreshView {
var animationStage: AnimationStage?
class func XIB_VIEW() -> BeelineView? {
let subviewArray = NSBundle.mainBundle().loadNibNamed("BeelineView", owner: self, options: nil)
return subviewArray.first as? BeelineView
let subviewArray = Bundle.main.loadNibNamed("BeelineView", owner: self, options: nil)
return subviewArray?.first as? BeelineView
}
// MARK: - Private
func hideBigIcons(hide: Bool) {
for iV in bigIcons { iV.hidden = hide }
func hideBigIcons(_ hide: Bool) {
for iV in bigIcons { iV.isHidden = hide }
}
func hideMediumIcons(hide: Bool) {
for iV in mediumIcons { iV.hidden = hide }
func hideMediumIcons(_ hide: Bool) {
for iV in mediumIcons { iV.isHidden = hide }
}
func hideSmallIcons(hide: Bool) {
for iV in smallIcons { iV.hidden = hide }
func hideSmallIcons(_ hide: Bool) {
for iV in smallIcons { iV.isHidden = hide }
}
@objc func executeAnimation() {
@ -54,20 +54,20 @@ class BeelineView: RMRPullToRefreshView {
return
}
hideBigIcons(animationStage == .Stage4)
hideMediumIcons(animationStage == .Stage3 || animationStage == .Stage4 || animationStage == .Stage5)
hideSmallIcons(animationStage != .Stage1)
hideBigIcons(animationStage == .stage4)
hideMediumIcons(animationStage == .stage3 || animationStage == .stage4 || animationStage == .stage5)
hideSmallIcons(animationStage != .stage1)
if let stage = animationStage {
animationStage = AnimationStage(rawValue: (stage.rawValue+1)%AnimationStage.count)
}
performSelector(#selector(executeAnimation), withObject: nil, afterDelay: 0.4)
perform(#selector(executeAnimation), with: nil, afterDelay: 0.4)
}
// MARK: - RMRPullToRefreshViewProtocol
override func didChangeDraggingProgress(progress: CGFloat) {
override func didChangeDraggingProgress(_ progress: CGFloat) {
hideBigIcons(progress < 0.33)
hideMediumIcons(progress < 0.66)
hideSmallIcons(progress < 0.99)
@ -75,11 +75,11 @@ class BeelineView: RMRPullToRefreshView {
override func beginLoadingAnimation() {
animationIsCanceled = false
animationStage = .Stage1
animationStage = .stage1
executeAnimation()
}
override func didEndLoadingAnimation(hidden: Bool) {
override func didEndLoadingAnimation(_ hidden: Bool) {
animationIsCanceled = true
}
}

View File

@ -16,37 +16,37 @@ class PerekrestokView: RMRPullToRefreshView {
var fromValue: CGFloat = 0.0
class func XIB_VIEW() -> PerekrestokView? {
let subviewArray = NSBundle.mainBundle().loadNibNamed("PerekrestokView", owner: self, options: nil)
return subviewArray.first as? PerekrestokView
let subviewArray = Bundle.main.loadNibNamed("PerekrestokView", owner: self, options: nil)
return subviewArray?.first as? PerekrestokView
}
// MARK: - Private
func angle(progress: CGFloat) -> CGFloat {
return -CGFloat(M_PI)/progress
func angle(_ progress: CGFloat) -> CGFloat {
return -.pi/progress
}
// MARK: - RMRPullToRefreshViewProtocol
override func didChangeDraggingProgress(progress: CGFloat) {
logoImageView.transform = CGAffineTransformMakeRotation(angle(progress));
override func didChangeDraggingProgress(_ progress: CGFloat) {
logoImageView.transform = CGAffineTransform(rotationAngle: angle(progress));
}
override func prepareForLoadingAnimation(startProgress: CGFloat) {
override func prepareForLoadingAnimation(_ startProgress: CGFloat) {
fromValue = angle(startProgress)
}
override func beginLoadingAnimation() {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.fromValue = fromValue
rotationAnimation.byValue = 2*M_PI
rotationAnimation.byValue = 2*Double.pi
rotationAnimation.duration = 0.9
rotationAnimation.repeatCount = HUGE
self.logoImageView.layer.addAnimation(rotationAnimation, forKey: "transformAnimation")
self.logoImageView.layer.add(rotationAnimation, forKey: "transformAnimation")
}
override func didEndLoadingAnimation(hidden: Bool) {
self.logoImageView.layer.removeAnimationForKey("transformAnimation")
override func didEndLoadingAnimation(_ hidden: Bool) {
self.logoImageView.layer.removeAnimation(forKey: "transformAnimation")
}
}

View File

@ -8,26 +8,25 @@
import UIKit
class TableViewController: UITableViewController {
final class TableViewController: UITableViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
super.prepareForSegue(segue, sender: sender)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let identifier = segue.identifier, let controller = segue.destinationViewController as? ViewController {
if let identifier = segue.identifier, let controller = segue.destination as? ViewController {
switch identifier {
case "perekrestok_top":
controller.exampleType = .PerekrestokTop
controller.exampleType = .perekrestokTop
case "perekrestok_bottom":
controller.exampleType = .PerekrestokBottom
controller.exampleType = .perekrestokBottom
case "beeline_top":
controller.exampleType = .BeelineTop
controller.exampleType = .beelineTop
case "beeline_bottom":
controller.exampleType = .BeelineBottom
controller.exampleType = .beelineBottom
case "redmadrobot_top":
controller.exampleType = .RedmadrobotTop
controller.exampleType = .redmadrobotTop
case "redmadrobot_bottom":
controller.exampleType = .RedmadrobotBottom
controller.exampleType = .redmadrobotBottom
default:
break
}

View File

@ -10,28 +10,56 @@ import UIKit
import RMRPullToRefresh
public enum ExampleType: Int {
case PerekrestokTop
case PerekrestokBottom
case BeelineTop
case BeelineBottom
case RedmadrobotTop
case RedmadrobotBottom
case perekrestokTop
case perekrestokBottom
case beelineTop
case beelineBottom
case redmadrobotTop
case redmadrobotBottom
}
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UIActionSheetDelegate {
final class ViewController: UIViewController {
// MARK: - Public properties
var exampleType: ExampleType = .beelineBottom
// MARK: - Private properites
private var pullToRefresh: RMRPullToRefresh?
private let formatter = DateFormatter()
private var items: [String] = []
private var count = 2
private var result = RMRPullToRefreshResultType.success
// MARK: - IBOutlets
@IBOutlet weak var tableView: UITableView!
var exampleType: ExampleType = .BeelineBottom
// MARK: - IBActions
var pullToRefresh: RMRPullToRefresh?
@IBAction func settings(_ sender: AnyObject) {
let alertController = UIAlertController(title: "Result type", message: nil, preferredStyle: .actionSheet)
let successAction = UIAlertAction(title: "Success", style: .default) { _ in
self.result = .noUpdates
}
alertController.addAction(successAction)
let noUpdatesAction = UIAlertAction(title: "No updates", style: .default) { _ in
self.result = .noUpdates
}
alertController.addAction(noUpdatesAction)
let errorAction = UIAlertAction(title: "Error", style: .default) { _ in
self.result = .error
}
alertController.addAction(errorAction)
present(alertController, animated: true, completion: nil)
}
let formatter = NSDateFormatter()
var items: [String] = []
var count = 2
var result = RMRPullToRefreshResultType.Success
// MARK: - UIViewController
override func viewDidLoad() {
super.viewDidLoad()
@ -44,11 +72,10 @@ class ViewController: UIViewController, UITableViewDataSource, UITableViewDelega
// MARK: - Pull to Refresh
func configurePullToRefresh() {
pullToRefresh = RMRPullToRefresh(scrollView: tableView, position: position()) { [weak self] _ in
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), {
if self?.result == .Success {
private func configurePullToRefresh() {
pullToRefresh = RMRPullToRefresh(scrollView: tableView, position: position()) { [weak self] in
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5, execute: {
if self?.result == .success {
self?.loadMore()
}
if let result = self?.result {
@ -57,113 +84,91 @@ class ViewController: UIViewController, UITableViewDataSource, UITableViewDelega
})
}
if exampleType == .PerekrestokTop || exampleType == .PerekrestokBottom {
switch exampleType {
case .perekrestokTop, .perekrestokBottom:
perekrestok()
} else if exampleType == .BeelineTop || exampleType == .BeelineBottom {
case .beelineTop, .beelineBottom:
beeline()
} else if exampleType == .RedmadrobotTop || exampleType == .RedmadrobotBottom {
case .redmadrobotTop, .redmadrobotBottom:
redmadrobot()
}
pullToRefresh?.setHideDelay(5.0, result: .Success)
pullToRefresh?.hideWhenError = false
//pullToRefresh?.setHideDelay(5.0, result: .success)
//pullToRefresh?.hideWhenError = false
}
// MARK: - Build example values
func perekrestok() {
private func perekrestok() {
if let pullToRefreshView = PerekrestokView.XIB_VIEW() {
pullToRefresh?.configureView(pullToRefreshView, state: .Dragging, result: .Success)
pullToRefresh?.configureView(pullToRefreshView, state: .Loading, result: .Success)
pullToRefresh?.configureView(pullToRefreshView, state: .dragging, result: .success)
pullToRefresh?.configureView(pullToRefreshView, state: .loading, result: .success)
}
pullToRefresh?.height = 90.0
pullToRefresh?.backgroundColor = UIColor(red: 16.0/255.0,
green: 192.0/255.0,
blue: 119.0/255.0,
alpha: 1.0)
pullToRefresh?.backgroundColor = UIColor(
red: 16.0/255.0,
green: 192.0/255.0,
blue: 119.0/255.0,
alpha: 1.0)
}
func beeline() {
private func beeline() {
if let pullToRefreshView = BeelineView.XIB_VIEW() {
pullToRefresh?.configureView(pullToRefreshView, state: .Dragging, result: .Success)
pullToRefresh?.configureView(pullToRefreshView, state: .Loading, result: .Success)
pullToRefresh?.configureView(pullToRefreshView, state: .dragging, result: .success)
pullToRefresh?.configureView(pullToRefreshView, state: .loading, result: .success)
}
pullToRefresh?.height = 90.0
pullToRefresh?.backgroundColor = UIColor.whiteColor()
pullToRefresh?.backgroundColor = UIColor.white
}
func redmadrobot() {
private func redmadrobot() {
pullToRefresh?.setupDefaultSettings()
}
func position() -> RMRPullToRefreshPosition {
if exampleType == .PerekrestokTop || exampleType == .BeelineTop || exampleType == .RedmadrobotTop {
return .Top
private func position() -> RMRPullToRefreshPosition {
if exampleType == .perekrestokTop || exampleType == .beelineTop || exampleType == .redmadrobotTop {
return .top
}
return .Bottom
return .bottom
}
// MARK: - Configure
func someConfiguring() {
formatter.dateStyle = NSDateFormatterStyle.LongStyle
formatter.timeStyle = .MediumStyle
}
// MARK: - Action
@IBAction func settings(sender: AnyObject) {
UIActionSheet(title: "Result type", delegate: self, cancelButtonTitle: nil, destructiveButtonTitle: nil, otherButtonTitles: ".Success", ".NoUpdates", ".Error").showInView(self.view)
}
// MARK: - UIActionSheetDelegate
func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int) {
switch buttonIndex {
case 0:
self.result = .Success
case 1:
self.result = .NoUpdates
case 2:
self.result = .Error
default:
break;
}
private func someConfiguring() {
formatter.dateStyle = DateFormatter.Style.long
formatter.timeStyle = .medium
}
// MARK: - Test data
func loadData() {
private func loadData() {
for _ in 0...count {
items.append(formatter.stringFromDate(NSDate()))
items.append(formatter.string(from: Date()))
}
}
func loadMore() {
private func loadMore() {
for _ in 0...20 {
self.items.append(formatter.stringFromDate(NSDate(timeIntervalSinceNow: 20)))
self.items.append(formatter.string(from: Date(timeIntervalSinceNow: 20)))
}
self.tableView.reloadData()
}
}
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
// MARK: - TableView
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = items[(indexPath as NSIndexPath).row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
}

View File

@ -7,10 +7,10 @@
objects = {
/* Begin PBXBuildFile section */
2CEF3E7B42820531D2D97DCC /* Pods_RMRPullToRefreshExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4DF5731A80639276BB33DCA /* Pods_RMRPullToRefreshExample.framework */; };
8921D4F51CA425B4000D28E3 /* PerekrestokView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8921D4F41CA425B4000D28E3 /* PerekrestokView.swift */; };
8921D4F71CA425C0000D28E3 /* PerekrestokView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8921D4F61CA425C0000D28E3 /* PerekrestokView.xib */; };
8952BE3E1CBACA1D00D94689 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8952BE3D1CBACA1D00D94689 /* TableViewController.swift */; };
89B014751CBAE22F002AB1B7 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 89B014741CBAE22F002AB1B7 /* Pods.framework */; };
89B014771CBAE3D5002AB1B7 /* RMRPullToRefresh.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 89B014761CBAE3D5002AB1B7 /* RMRPullToRefresh.framework */; };
89B06BF01CBA8A4900485A08 /* BeelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89B06BEF1CBA8A4900485A08 /* BeelineView.swift */; };
89B06BF21CBA8B0700485A08 /* BeelineView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 89B06BF11CBA8B0700485A08 /* BeelineView.xib */; };
@ -22,11 +22,11 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
51A8D36C5CCC1E18D2666CD6 /* Pods-RMRPullToRefreshExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RMRPullToRefreshExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RMRPullToRefreshExample/Pods-RMRPullToRefreshExample.debug.xcconfig"; sourceTree = "<group>"; };
766D53F6605D8961328E54A1 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
8921D4F41CA425B4000D28E3 /* PerekrestokView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerekrestokView.swift; sourceTree = "<group>"; };
8921D4F61CA425C0000D28E3 /* PerekrestokView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PerekrestokView.xib; sourceTree = "<group>"; };
8952BE3D1CBACA1D00D94689 /* TableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = "<group>"; };
89B014741CBAE22F002AB1B7 /* Pods.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pods.framework; path = "../../../../Library/Developer/Xcode/DerivedData/RMRPullToRefresh-fwnpioedcaituoahusazwcgryphj/Build/Products/Debug-iphonesimulator/Pods.framework"; sourceTree = "<group>"; };
89B014761CBAE3D5002AB1B7 /* RMRPullToRefresh.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RMRPullToRefresh.framework; path = "../../../../Library/Developer/Xcode/DerivedData/RMRPullToRefresh-fwnpioedcaituoahusazwcgryphj/Build/Products/Debug-iphonesimulator/RMRPullToRefresh.framework"; sourceTree = "<group>"; };
89B06BEF1CBA8A4900485A08 /* BeelineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BeelineView.swift; sourceTree = "<group>"; };
89B06BF11CBA8B0700485A08 /* BeelineView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BeelineView.xib; sourceTree = "<group>"; };
@ -37,6 +37,8 @@
89CB123E1C9DA07B00048E46 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
89CB12411C9DA07B00048E46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
89CB12431C9DA07B00048E46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9373B01A0C7A0314295D05D2 /* Pods-RMRPullToRefreshExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RMRPullToRefreshExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-RMRPullToRefreshExample/Pods-RMRPullToRefreshExample.release.xcconfig"; sourceTree = "<group>"; };
A4DF5731A80639276BB33DCA /* Pods_RMRPullToRefreshExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RMRPullToRefreshExample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D48C26E126E3942BBDFD40CF /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -46,7 +48,7 @@
buildActionMask = 2147483647;
files = (
89B014771CBAE3D5002AB1B7 /* RMRPullToRefresh.framework in Frameworks */,
89B014751CBAE22F002AB1B7 /* Pods.framework in Frameworks */,
2CEF3E7B42820531D2D97DCC /* Pods_RMRPullToRefreshExample.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -58,6 +60,8 @@
children = (
766D53F6605D8961328E54A1 /* Pods.debug.xcconfig */,
D48C26E126E3942BBDFD40CF /* Pods.release.xcconfig */,
51A8D36C5CCC1E18D2666CD6 /* Pods-RMRPullToRefreshExample.debug.xcconfig */,
9373B01A0C7A0314295D05D2 /* Pods-RMRPullToRefreshExample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
@ -111,7 +115,7 @@
isa = PBXGroup;
children = (
89B014761CBAE3D5002AB1B7 /* RMRPullToRefresh.framework */,
89B014741CBAE22F002AB1B7 /* Pods.framework */,
A4DF5731A80639276BB33DCA /* Pods_RMRPullToRefreshExample.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -123,12 +127,11 @@
isa = PBXNativeTarget;
buildConfigurationList = 89CB12461C9DA07B00048E46 /* Build configuration list for PBXNativeTarget "RMRPullToRefreshExample" */;
buildPhases = (
00D4D85E4DF2FF039A017773 /* Check Pods Manifest.lock */,
00D4D85E4DF2FF039A017773 /* [CP] Check Pods Manifest.lock */,
89CB12301C9DA07B00048E46 /* Sources */,
89CB12311C9DA07B00048E46 /* Frameworks */,
89CB12321C9DA07B00048E46 /* Resources */,
2BE863C51DEA329C1234F1B0 /* Embed Pods Frameworks */,
C137BE069515EFF1A69455DF /* Copy Pods Resources */,
2BE863C51DEA329C1234F1B0 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -146,11 +149,12 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0720;
LastUpgradeCheck = 1000;
ORGANIZATIONNAME = "Merkulov Ilya";
TargetAttributes = {
89CB12331C9DA07B00048E46 = {
CreatedOnToolsVersion = 7.2.1;
LastSwiftMigration = 0920;
};
};
};
@ -188,49 +192,40 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
00D4D85E4DF2FF039A017773 /* Check Pods Manifest.lock */ = {
00D4D85E4DF2FF039A017773 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "Check Pods Manifest.lock";
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RMRPullToRefreshExample-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
2BE863C51DEA329C1234F1B0 /* Embed Pods Frameworks */ = {
2BE863C51DEA329C1234F1B0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-RMRPullToRefreshExample/Pods-RMRPullToRefreshExample-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/RMRPullToRefresh/RMRPullToRefresh.framework",
);
name = "Embed Pods Frameworks";
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RMRPullToRefresh.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
C137BE069515EFF1A69455DF /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RMRPullToRefreshExample/Pods-RMRPullToRefreshExample-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
@ -278,13 +273,23 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -306,7 +311,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.2;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -322,13 +327,23 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -344,36 +359,43 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.2;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
89CB12471C9DA07B00048E46 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 766D53F6605D8961328E54A1 /* Pods.debug.xcconfig */;
baseConfigurationReference = 51A8D36C5CCC1E18D2666CD6 /* Pods-RMRPullToRefreshExample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = GMD7EK7S94;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = RMRPullToRefresh/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.redmadrobot.RMRPullToRefresh;
PRODUCT_NAME = RMRPullToRefreshExample;
SWIFT_VERSION = 4.2;
};
name = Debug;
};
89CB12481C9DA07B00048E46 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D48C26E126E3942BBDFD40CF /* Pods.release.xcconfig */;
baseConfigurationReference = 9373B01A0C7A0314295D05D2 /* Pods-RMRPullToRefreshExample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = GMD7EK7S94;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = RMRPullToRefresh/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.redmadrobot.RMRPullToRefresh;
PRODUCT_NAME = RMRPullToRefreshExample;
SWIFT_VERSION = 4.2;
};
name = Release;
};

17
LICENSE Normal file
View File

@ -0,0 +1,17 @@
Copyright (c) 2016 Ilya Merkulov im@redmadrobot.com
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.

150
README.md
View File

@ -0,0 +1,150 @@
# RMRPullToRefresh
==
Репозиторий pull to refresh контрола для UIScrollView, UITableView, UICollectionView для платформы iOS.
<img src="https://dl.dropboxusercontent.com/u/69633554/RMRPullToRefresh/pullSuccessBottom.gif" width="262" height="468">
<img src="https://dl.dropboxusercontent.com/u/69633554/RMRPullToRefresh/pullNoUpdatesTop.gif" width="262" height="468">
<img src="https://dl.dropboxusercontent.com/u/69633554/RMRPullToRefresh/pullErrorTop.gif" width="262" height="468">
Как установить?
--------
`pod 'RMRPullToRefresh', :git => "git@git.redmadrobot.com:helper-ios/RMRPullToRefresh.git"`
Как добавить?
--------
```swift
import RMRPullToRefresh
var pullToRefresh: RMRPullToRefresh?
pullToRefresh = RMRPullToRefresh(scrollView: tableView,
position: .Top) { [weak self] _ in // .Top или .Bottom
// Загрузка данных
self?.service.load() { _ in
// Завершение загрузки
self?.pullToRefresh?.stopLoading()
})
}
```
Чтобы завершить анимацию, необходимо выполнить:
```swift
pullToRefresh?.stopLoading() // Завершиться с результатом .Success
```
или
```swift
pullToRefresh?.stopLoading(.Success) // .Success, .NoUpdates, .Error
```
Позиции
--------
```swift
public enum RMRPullToRefreshPosition: Int {
case Top // Для добавления контрола сверху скрола
case Bottom // Для добавления контроль снизу скрола
}
```
Состояния
--------
```swift
public enum RMRPullToRefreshState: Int {
case Stopped // Нет скролинга, нет загрузки
case Dragging // Скролинг
case Loading // Загрузка
}
```
Тип результата
--------
```swift
public enum RMRPullToRefreshResultType: Int {
case Success // Загрузка завершилась успешно и есть обновления
case NoUpdates // Загрузка завершилась успешно, но обновлений нет
case Error // Загрузка завершилась с ошибкой
}
```
Как кастомизировать?
--------
Существует два метода для конфигурации кастомных view:
```swift
func configureView(view :RMRPullToRefreshView, state:RMRPullToRefreshState, result:RMRPullToRefreshResultType)
```
```swift
func configureView(view :RMRPullToRefreshView, result:RMRPullToRefreshResultType) // Будет сконфигурировано для состояний .Loading, .Dragging и .Stopped
```
Пример:
```swift
// Конфигурируем view для состояний .Dragging и .Loading для результата .Success
if let pullToRefreshView = BeelineView.XIB_VIEW() {
pullToRefresh?.configureView(pullToRefreshView, state: .Dragging, result: .Success)
pullToRefresh?.configureView(pullToRefreshView, state: .Loading, result: .Success)
}
```
Кастомная view должна наследоваться от класса RMRPullToRefreshView и для анимирования должны быть реализованы методы протокола RMRPullToRefreshViewProtocol:
```swift
// Подготовка к анимации загрузки
// Вызываться перед beginLoadingAnimation()
public func prepareForLoadingAnimation(startProgress: CGFloat) {}
// Начало анимации
public func beginLoadingAnimation() {}
// Завершение анимации
// Будет вызываться сразу после завершения загрузки
public func willEndLoadingAnimation() {}
// Завершение анимации
// Будет вызывать через время, равное hideDelay после завершения загрузки
// @param hidden - флаг будет скрыт контрол или нет после завершения анимации
public func didEndLoadingAnimation(hidden: Bool) {}
// Изменения прогресса скролинга
public func didChangeDraggingProgress(progress: CGFloat) {}
```
Настройки
--------
Для задания RedMadRobot дизайна:
```swift
pullToRefresh?.setupDefaultSettings()
```
Изменить высоту (по умолчанию 90.0):
```swift
pullToRefresh?.height = 70.0
```
Изменить цвет бэкграунда (по умолчанию whiteColor()):
```swift
pullToRefresh?.backgroundColor = UIColor(red: 16.0/255.0,
green: 192.0/255.0,
blue: 119.0/255.0,
alpha: 1.0)
```
Для задания времени закрытия контрола (по умолчанию 0.0):
```swift
pullToRefresh?.setHideDelay(5.0, result: .Success) // .Success, .NoUpdates, .Error
```
Если не хотим скрывать контрол с ошибкой (не забудьте установить view для .Error):
```swift
pullToRefresh?.hideWhenError = false
```
<img src="https://dl.dropboxusercontent.com/u/69633554/RMRPullToRefresh/pullErrorTopNoHide.gif" width="262" height="468">

View File

@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "RMRPullToRefresh"
spec.version = "0.1"
spec.platform = :ios, "8.0"
spec.version = "0.8.0"
spec.platform = :ios, "9.0"
spec.license = { :type => "MIT", :file => "LICENSE" }
spec.summary = "A pull to refresh control for UIScrollView (UITableView and UICollectionView)"
spec.homepage = "http://redmadrobot.com/"
spec.author = "Ilya Merkulov"
spec.source = { :git => :"https://git.redmadrobot.com/im/RMRPullToRefresh", :tag => "v0.1" }
spec.source = { :git => "https://git.redmadrobot.com/helper-ios/RMRPullToRefresh.git", :tag => spec.version }
spec.source_files = "Classes/*.{swift}", "Classes/Default/*.{swift}"
spec.resources = ['Images/*.png']
spec.resources = ['Images/*.png']
end