├── .gitignore
├── .gitmodules
├── ITPullToRefreshScrollView
├── Classes
│ ├── DuxScrollViewAnimation.h
│ ├── DuxScrollViewAnimation.m
│ ├── ITPullToRefreshClipView.h
│ ├── ITPullToRefreshClipView.m
│ ├── ITPullToRefreshEdgeView.h
│ ├── ITPullToRefreshEdgeView.m
│ ├── ITPullToRefreshScrollView.h
│ └── ITPullToRefreshScrollView.m
├── ITPullToRefreshScrollView.xcodeproj
│ └── project.pbxproj
├── ITPullToRefreshScrollView
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Base.lproj
│ │ └── MainMenu.xib
│ ├── ITPullToRefreshScrollView-Info.plist
│ ├── ITPullToRefreshScrollView-Prefix.pch
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── en.lproj
│ │ ├── Credits.rtf
│ │ └── InfoPlist.strings
│ └── main.m
└── ITPullToRefreshScrollViewTests
│ ├── ITPullToRefreshScrollViewTests-Info.plist
│ ├── ITPullToRefreshScrollViewTests.m
│ └── en.lproj
│ └── InfoPlist.strings
├── LICENSE
├── README.md
└── demo.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | .DS_Store
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | *.xcworkspace
13 | !default.xcworkspace
14 | xcuserdata
15 | profile
16 | *.moved-aside
17 | DerivedData
18 | .idea/
19 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Modules/NSBKeyframeAnimation"]
2 | path = Modules/NSBKeyframeAnimation
3 | url = https://github.com/iluuu1994/NSBKeyframeAnimation
4 | [submodule "Modules/ITProgressIndicator"]
5 | path = Modules/ITProgressIndicator
6 | url = https://github.com/iluuu1994/ITProgressIndicator
7 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/Classes/DuxScrollViewAnimation.h:
--------------------------------------------------------------------------------
1 | //
2 | // DuxScrollViewAnimation.h
3 | // Dux
4 | //
5 | // Created by Abhi Beckert on 2011-11-30.
6 | //
7 | // This is free and unencumbered software released into the public domain.
8 | // For more information, please refer to
9 | //
10 |
11 | #import
12 |
13 | @interface DuxScrollViewAnimation : NSAnimation
14 |
15 | @property (retain) NSScrollView *scrollView;
16 | @property NSPoint originPoint;
17 | @property NSPoint targetPoint;
18 |
19 | + (void)animatedScrollPointToCenter:(NSPoint)targetPoint inScrollView:(NSScrollView *)scrollView;
20 | + (void)animatedScrollToPoint:(NSPoint)targetPoint inScrollView:(NSScrollView *)scrollView;
21 | + (void)animatedScrollToPoint:(NSPoint)targetPoint inScrollView:(NSScrollView *)scrollView delegate:(id)delegate;
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/Classes/DuxScrollViewAnimation.m:
--------------------------------------------------------------------------------
1 | //
2 | // DuxScrollViewAnimation.m
3 | // Dux
4 | //
5 | // Created by Abhi Beckert on 2011-11-30.
6 | //
7 | // This is free and unencumbered software released into the public domain.
8 | // For more information, please refer to
9 | //
10 |
11 | #import "DuxScrollViewAnimation.h"
12 |
13 | @implementation DuxScrollViewAnimation
14 |
15 | @synthesize scrollView;
16 | @synthesize originPoint;
17 | @synthesize targetPoint;
18 |
19 | + (void)animatedScrollPointToCenter:(NSPoint)targetPoint inScrollView:(NSScrollView *)scrollView
20 | {
21 | NSRect visibleRect = scrollView.documentVisibleRect;
22 |
23 | targetPoint = NSMakePoint(targetPoint.x - (NSWidth(visibleRect) / 2), targetPoint.y - (NSHeight(visibleRect) / 2));
24 |
25 | [self animatedScrollToPoint:targetPoint inScrollView:scrollView];
26 | }
27 |
28 | + (void)animatedScrollToPoint:(NSPoint)targetPoint inScrollView:(NSScrollView *)scrollView {
29 | [self animatedScrollToPoint:targetPoint inScrollView:scrollView delegate:nil];
30 | }
31 |
32 | + (void)animatedScrollToPoint:(NSPoint)targetPoint inScrollView:(NSScrollView *)scrollView delegate:(id)delegate;
33 | {
34 | DuxScrollViewAnimation *animation = [[DuxScrollViewAnimation alloc] initWithDuration:0.2 animationCurve:NSAnimationEaseInOut];
35 |
36 | animation.delegate = delegate;
37 | animation.scrollView = scrollView;
38 | animation.originPoint = scrollView.documentVisibleRect.origin;
39 | animation.targetPoint = targetPoint;
40 |
41 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
42 | [animation startAnimation];
43 | });
44 | }
45 |
46 | - (void)setCurrentProgress:(NSAnimationProgress)progress
47 | {
48 | typedef float (^MyAnimationCurveBlock)(float, float, float);
49 | MyAnimationCurveBlock cubicEaseInOut = ^ float (float t, float start, float end) {
50 | t *= 2.;
51 | if (t < 1.) return end/2 * t * t * t + start - 1.f;
52 | t -= 2;
53 | return end/2*(t * t * t + 2) + start - 1.f;
54 | };
55 |
56 | dispatch_sync(dispatch_get_main_queue(), ^{
57 | NSPoint progressPoint = self.originPoint;
58 | progressPoint.x += cubicEaseInOut(progress, 0, self.targetPoint.x - self.originPoint.x);
59 | progressPoint.y += cubicEaseInOut(progress, 0, self.targetPoint.y - self.originPoint.y);
60 |
61 | [self.scrollView.documentView scrollPoint:progressPoint];
62 | [self.scrollView displayIfNeeded];
63 | });
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/Classes/ITPullToRefreshClipView.h:
--------------------------------------------------------------------------------
1 | //
2 | // ITPullToRefreshClipView.h
3 | // ITPullToRefreshScrollView
4 | //
5 | // Created by Ilija Tovilo on 9/25/13.
6 | // Copyright (c) 2013 Ilija Tovilo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /**
12 | * This class is used by `ITPullToRefreshScrollView`.
13 | * You don't need to interact with it directly.
14 | */
15 | @interface ITPullToRefreshClipView : NSClipView
16 | @end
17 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/Classes/ITPullToRefreshClipView.m:
--------------------------------------------------------------------------------
1 | //
2 | // ITPullToRefreshClipView.m
3 | // ITPullToRefreshScrollView
4 | //
5 | // Created by Ilija Tovilo on 9/25/13.
6 | // Copyright (c) 2013 Ilija Tovilo. All rights reserved.
7 | //
8 |
9 | #import "ITPullToRefreshClipView.h"
10 | #import "ITPullToRefreshScrollView.h"
11 | #import "ITPullToRefreshEdgeView.h"
12 |
13 | @implementation ITPullToRefreshClipView
14 |
15 | #pragma mark - Properties
16 |
17 | -(NSView *) headerView { return [(ITPullToRefreshScrollView *)self.superview edgeViewForEdge:ITPullToRefreshEdgeTop]; }
18 | -(NSView*) footerView { return [(ITPullToRefreshScrollView *)self.superview edgeViewForEdge:ITPullToRefreshEdgeBottom]; }
19 | -(NSUInteger)refreshingSides { return [(ITPullToRefreshScrollView *)self.superview refreshingEdges]; }
20 |
21 |
22 | #pragma mark - NSClipView
23 |
24 | - (NSPoint)constrainScrollPoint:(NSPoint)proposedNewOrigin
25 | {
26 | NSPoint constrained = [super constrainScrollPoint:proposedNewOrigin];
27 | const NSRect clipViewBounds = self.bounds;
28 | NSView* const documentView = self.documentView;
29 | const NSRect documentFrame = documentView.frame;
30 |
31 | const NSUInteger refreshingSides = [self refreshingSides];
32 |
33 | if (refreshingSides & ITPullToRefreshEdgeTop && proposedNewOrigin.y <= 0) {
34 | const NSRect headerFrame = [self headerView].frame;
35 | constrained.y = MAX(-headerFrame.size.height, proposedNewOrigin.y);
36 | }
37 |
38 | if((refreshingSides & ITPullToRefreshEdgeBottom) ) {
39 | const NSRect footerFrame = [self footerView].frame;
40 | if (proposedNewOrigin.y > documentFrame.size.height - clipViewBounds.size.height) {
41 | const CGFloat maxHeight = documentFrame.size.height - clipViewBounds.size.height + footerFrame.size.height + 1;
42 | constrained.y = MIN(maxHeight, proposedNewOrigin.y);
43 | }
44 | }
45 |
46 | return constrained;
47 | }
48 |
49 | -(NSRect)documentRect
50 | {
51 | NSRect documentRect = [super documentRect];
52 |
53 | const NSUInteger refreshingSides = [self refreshingSides];
54 |
55 | if (refreshingSides & ITPullToRefreshEdgeTop) {
56 | const NSRect headerFrame = [self headerView].frame;
57 | documentRect.size.height += headerFrame.size.height;
58 | documentRect.origin.y -= headerFrame.size.height;
59 | }
60 |
61 | if(refreshingSides & ITPullToRefreshEdgeBottom) {
62 | const NSRect footerFrame = [self footerView].frame;
63 | documentRect.size.height += footerFrame.size.height ;
64 | }
65 |
66 | return documentRect;
67 | }
68 |
69 |
70 | #pragma mark - NSView
71 |
72 | -(BOOL)isFlipped
73 | {
74 | return YES;
75 | }
76 |
77 |
78 | @end
79 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/Classes/ITPullToRefreshEdgeView.h:
--------------------------------------------------------------------------------
1 | //
2 | // ITPullToRefreshEdgeView.h
3 | // ITPullToRefreshScrollView
4 | //
5 | // Created by Ilija Tovilo on 9/25/13.
6 | // Copyright (c) 2013 Ilija Tovilo. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ITPullToRefreshScrollView.h"
11 |
12 | /**
13 | * This view is used by the `ITPullToRefreshScrollView` to display the edges.
14 | * You can extend from this class to create custom edges.
15 | */
16 | @interface ITPullToRefreshEdgeView : NSView
17 |
18 | /**
19 | * Use this method to initialise an edge view
20 | *
21 | * @param edge - The edge which will the edge view be used for
22 | *
23 | * @return A new instance of `ITPullToRefreshEdgeView`
24 | */
25 | - (instancetype)initWithEdge:(ITPullToRefreshEdge)edge;
26 |
27 |
28 |
29 | // --------------------------
30 | // ------------------- Events
31 | // --------------------------
32 |
33 | /**
34 | * This method is called when the edge is triggered
35 | *
36 | * @param scrollView - The sender scroll view
37 | */
38 | - (void)pullToRefreshScrollViewDidTriggerRefresh:(ITPullToRefreshScrollView *)scrollView;
39 |
40 | /**
41 | * This method is called when the edge is untriggered
42 | *
43 | * @param scrollView - The sender scroll view
44 | */
45 | - (void)pullToRefreshScrollViewDidUntriggerRefresh:(ITPullToRefreshScrollView *)scrollView;
46 |
47 | /**
48 | * This method is called when the edge starts refreshing
49 | *
50 | * @param scrollView - The sender scroll view
51 | */
52 | - (void)pullToRefreshScrollViewDidStartRefreshing:(ITPullToRefreshScrollView *)scrollView;
53 |
54 | /**
55 | * This method is called when the edge stops refreshing
56 | *
57 | * @param scrollView - The sender scroll view
58 | */
59 | - (void)pullToRefreshScrollViewDidStopRefreshing:(ITPullToRefreshScrollView *)scrollView;
60 |
61 | /**
62 | * This is the final method called when the scroll view stopped animating
63 | *
64 | * @param scrollView - The sender scroll view
65 | */
66 | - (void)pullToRefreshScrollViewDidStopAnimating:(ITPullToRefreshScrollView *)scrollView;
67 |
68 | /**
69 | * This method is called when part of the edge view becomes visible
70 | *
71 | * @param scrollView - The sender scroll view
72 | * @param progress - The amount of the edge view that is visible (from 0.0 to 1.0)
73 | */
74 | - (void)pullToRefreshScrollView:(ITPullToRefreshScrollView *)scrollView didScrollWithProgress:(CGFloat)progress;
75 |
76 |
77 |
78 | // --------------------------
79 | // ------------ Customisation
80 | // --------------------------
81 |
82 | /**
83 | * Override this to remove the progress indicator and create other components
84 | */
85 | - (void)installComponents;
86 |
87 | /**
88 | * The height of the edge view.
89 | * Override this method to achieve a custom edge view height.
90 | *
91 | * @return The height of the edge view
92 | */
93 | - (CGFloat)edgeViewHeight;
94 |
95 | /**
96 | * Override this method to draw a custom background.
97 | * You can also just override `drawRect:` and to all the drawing on your own.
98 | *
99 | * @param The rect which should be drawn on
100 | */
101 | - (void)drawBackgroundInRect:(NSRect)dirtyRect;
102 |
103 |
104 | @property ITPullToRefreshEdge edgeViewEdge;
105 |
106 | @end
107 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/Classes/ITPullToRefreshEdgeView.m:
--------------------------------------------------------------------------------
1 | //
2 | // ITPullToRefreshEdgeView.m
3 | // ITPullToRefreshScrollView
4 | //
5 | // Created by Ilija Tovilo on 9/25/13.
6 | // Copyright (c) 2013 Ilija Tovilo. All rights reserved.
7 | //
8 |
9 | #import "ITPullToRefreshEdgeView.h"
10 | #import
11 | #import "ITProgressIndicator.h"
12 | #import "NSBKeyframeAnimation.h"
13 |
14 | #define kDefaultEdgeViewHeight 30
15 | #define kSpinnerSize 30
16 |
17 | #define kMinSpinAnimationDuration 2.0
18 | #define kMaxSpinAnimationDuration 8.0
19 | #define kSpringRange 0.4
20 |
21 | @interface ITPullToRefreshEdgeView () {
22 | CGFloat _cachedProgress;
23 | }
24 | @property (strong) ITProgressIndicator *progressIndicator;
25 | @end
26 |
27 | @implementation ITPullToRefreshEdgeView
28 |
29 |
30 | #pragma mark - Init
31 |
32 | - (instancetype)initWithEdge:(ITPullToRefreshEdge)edge {
33 | if (self = [super init]) {
34 | _edgeViewEdge = edge;
35 | [self installComponents];
36 | }
37 |
38 | return self;
39 | }
40 |
41 | - (void)installComponents {
42 | self.progressIndicator = [[ITProgressIndicator alloc] initWithFrame:NSMakeRect(0, 0, kSpinnerSize, kSpinnerSize)];
43 |
44 | [self.progressIndicator setWantsLayer:YES];
45 | self.progressIndicator.animates = NO;
46 | self.progressIndicator.hideWhenStopped = NO;
47 | self.progressIndicator.isIndeterminate = NO;
48 | self.progressIndicator.progress = 0.0;
49 | self.progressIndicator.numberOfLines = 12;
50 | self.progressIndicator.widthOfLine = 2.0;
51 | self.progressIndicator.innerMargin = 5;
52 | self.progressIndicator.color = [NSColor colorWithDeviceWhite:0.4 alpha:1.0];
53 | self.progressIndicator.layer.contentsGravity = kCAGravityCenter;
54 | [self.progressIndicator.layer setAnchorPoint:CGPointMake(0.5, 0.5)];
55 |
56 | [self addSubview:self.progressIndicator];
57 |
58 |
59 | // Install Layout Constraints
60 | {
61 | [self.progressIndicator setTranslatesAutoresizingMaskIntoConstraints:NO];
62 |
63 | [self.progressIndicator addConstraint:[NSLayoutConstraint constraintWithItem:self.progressIndicator
64 | attribute:NSLayoutAttributeWidth
65 | relatedBy:NSLayoutRelationEqual
66 | toItem:nil
67 | attribute:NSLayoutAttributeNotAnAttribute
68 | multiplier:1
69 | constant:kSpinnerSize]];
70 | [self.progressIndicator addConstraint:[NSLayoutConstraint constraintWithItem:self.progressIndicator
71 | attribute:NSLayoutAttributeHeight
72 | relatedBy:NSLayoutRelationEqual
73 | toItem:nil
74 | attribute:NSLayoutAttributeNotAnAttribute
75 | multiplier:1
76 | constant:kSpinnerSize]];
77 |
78 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.progressIndicator
79 | attribute:NSLayoutAttributeCenterY
80 | relatedBy:0
81 | toItem:self
82 | attribute:NSLayoutAttributeCenterY
83 | multiplier:1
84 | constant:0]];
85 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.progressIndicator
86 | attribute:NSLayoutAttributeCenterX
87 | relatedBy:0
88 | toItem:self
89 | attribute:NSLayoutAttributeCenterX
90 | multiplier:1
91 | constant:0]];
92 | }
93 | }
94 |
95 |
96 | #pragma mark - Constraints
97 |
98 | - (void)viewDidMoveToSuperview {
99 | [self setUpConstraints];
100 | }
101 |
102 | - (void)setUpConstraints {
103 | [self setTranslatesAutoresizingMaskIntoConstraints:NO];
104 |
105 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self
106 | attribute:NSLayoutAttributeHeight
107 | relatedBy:NSLayoutRelationEqual
108 | toItem:nil
109 | attribute:NSLayoutAttributeNotAnAttribute
110 | multiplier:1
111 | constant:[self edgeViewHeight]]];
112 |
113 | [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self
114 | attribute:NSLayoutAttributeLeft
115 | relatedBy:NSLayoutRelationEqual
116 | toItem:self.superview
117 | attribute:NSLayoutAttributeLeft
118 | multiplier:1.0
119 | constant:0.0]];
120 | [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self
121 | attribute:NSLayoutAttributeRight
122 | relatedBy:NSLayoutRelationEqual
123 | toItem:self.superview
124 | attribute:NSLayoutAttributeRight
125 | multiplier:1.0
126 | constant:0.0]];
127 |
128 | if (self.edgeViewEdge & ITPullToRefreshEdgeTop)
129 | {
130 | [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self
131 | attribute:NSLayoutAttributeBottom
132 | relatedBy:NSLayoutRelationEqual
133 | toItem:[(NSClipView *)self.superview documentView]
134 | attribute:NSLayoutAttributeTop
135 | multiplier:1.0
136 | constant:0.0]];
137 | }
138 | else if (self.edgeViewEdge & ITPullToRefreshEdgeBottom)
139 | {
140 | [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self
141 | attribute:NSLayoutAttributeTop
142 | relatedBy:NSLayoutRelationEqual
143 | toItem:[(NSClipView *)self.superview documentView]
144 | attribute:NSLayoutAttributeBottom
145 | multiplier:1.0
146 | constant:0.0]];
147 | }
148 | }
149 |
150 |
151 | #pragma mark - Customisation Methods
152 |
153 | - (CGFloat)edgeViewHeight {
154 | return kDefaultEdgeViewHeight;
155 | }
156 |
157 | - (void)drawRect:(NSRect)dirtyRect {
158 | [self drawBackgroundInRect:dirtyRect];
159 | }
160 |
161 | - (void)drawBackgroundInRect:(NSRect)dirtyRect {
162 | [[NSColor colorWithDeviceWhite:0.96 alpha:1.0] set];
163 | NSRectFill(dirtyRect);
164 | }
165 |
166 | - (void)pullToRefreshScrollView:(ITPullToRefreshScrollView *)scrollView didScrollWithProgress:(CGFloat)progress {
167 | _cachedProgress = progress;
168 |
169 | if (progress < 1.0) {
170 | self.progressIndicator.isIndeterminate = NO;
171 | self.progressIndicator.progress = progress;
172 | }
173 | }
174 |
175 | - (void)pullToRefreshScrollViewDidTriggerRefresh:(ITPullToRefreshScrollView *)scrollView {
176 | self.progressIndicator.isIndeterminate = NO;
177 | self.progressIndicator.progress = 1.0;
178 | }
179 |
180 | - (void)pullToRefreshScrollViewDidUntriggerRefresh:(ITPullToRefreshScrollView *)scrollView {
181 |
182 | }
183 |
184 | - (void)pullToRefreshScrollViewDidStartRefreshing:(ITPullToRefreshScrollView *)scrollView {
185 | CGFloat tension = (_cachedProgress - 1.0 <= kSpringRange)?_cachedProgress - 1:kSpringRange;
186 | CGFloat duration = kMaxSpinAnimationDuration - ((kMaxSpinAnimationDuration - kMinSpinAnimationDuration) * (1.0 / kSpringRange * tension));
187 |
188 | [self.progressIndicator.layer addAnimation:[self rotationAnimationWithDuration:duration] forKey:@"rotation"];
189 | self.progressIndicator.isIndeterminate = YES;
190 | self.progressIndicator.animates = YES;
191 | }
192 |
193 | - (void)pullToRefreshScrollViewDidStopRefreshing:(ITPullToRefreshScrollView *)scrollView {
194 | self.progressIndicator.animates = NO;
195 | self.progressIndicator.isIndeterminate = NO;
196 |
197 | [self.progressIndicator.layer addAnimation:[self shrinkAnimation] forKey:@"shrink"];
198 | }
199 |
200 | - (void)pullToRefreshScrollViewDidStopAnimating:(ITPullToRefreshScrollView *)scrollView {
201 | [self.progressIndicator.layer removeAnimationForKey:@"rotation"];
202 | }
203 |
204 | - (CAAnimation *)shrinkAnimation {
205 | NSBKeyframeAnimation *animation = [NSBKeyframeAnimation animationWithKeyPath:@"transform.scale"
206 | duration:0.3
207 | startValue:1
208 | endValue:0.0
209 | function:NSBKeyframeAnimationFunctionEaseOutCubic];
210 |
211 | return animation;
212 | }
213 |
214 | - (CAAnimation *)rotationAnimationWithDuration:(CGFloat)duration {
215 | NSBKeyframeAnimation *animation = [NSBKeyframeAnimation animationWithKeyPath:@"transform"
216 | duration:duration
217 | startValue:0
218 | endValue:-2 * M_PI
219 | function:NSBKeyframeAnimationFunctionEaseOutCubic];
220 |
221 | [animation setValueFunction:[CAValueFunction functionWithName: kCAValueFunctionRotateZ]];
222 |
223 | return animation;
224 | }
225 |
226 | @end
227 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/Classes/ITPullToRefreshScrollView.h:
--------------------------------------------------------------------------------
1 | //
2 | // ITPullToRefreshScrollView.h
3 | // ITPullToRefreshScrollView
4 | //
5 | // Created by Ilija Tovilo on 9/25/13.
6 | // Copyright (c) 2013 Ilija Tovilo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #ifndef ITPullToRefreshScrollViewConsts
12 | #define ITPullToRefreshScrollViewConsts
13 |
14 |
15 | /**
16 | * Enum that defines the edges of the ITPullToRefreshScrollView that can be refreshed
17 | */
18 | typedef NS_ENUM(NSUInteger, ITPullToRefreshEdge) {
19 | ITPullToRefreshEdgeNone = 0,
20 | ITPullToRefreshEdgeTop = 1,
21 | ITPullToRefreshEdgeBottom = 1 << 1
22 | };
23 |
24 | #endif
25 |
26 | @class ITPullToRefreshScrollView;
27 |
28 | /**
29 | * The delegate of the scroll view must implement this protocol.
30 | */
31 | @protocol ITPullToRefreshScrollViewDelegate
32 |
33 | @optional
34 | /**
35 | * This method get's invoked when the scroll view started refreshing
36 | *
37 | * @param scrollView - The scroll view that started refreshing
38 | * @param edge - The edge that started refreshing
39 | */
40 | - (void)pullToRefreshView:(ITPullToRefreshScrollView *)scrollView
41 | didStartRefreshingEdge:(ITPullToRefreshEdge)edge;
42 |
43 | /**
44 | * This method get's invoked when the scroll view stopped refreshing
45 | *
46 | * @param scrollView - The scroll view that stopped refreshing
47 | * @param edge - The edge that stopped refreshing
48 | */
49 | - (void)pullToRefreshView:(ITPullToRefreshScrollView *)scrollView
50 | didStopRefreshingEdge:(ITPullToRefreshEdge)edge;
51 |
52 | @end
53 |
54 |
55 | @class ITPullToRefreshEdgeView;
56 |
57 | /**
58 | * ITPullToRefreshScrollView is subclass of the NSScrollView class.
59 | * It supports refreshing by scrolling.
60 | */
61 | @interface ITPullToRefreshScrollView : NSScrollView
62 |
63 | /**
64 | * The delegate instance will receive notifications from the scroll view.
65 | * Look at the `ITPullToRefreshScrollViewDelegate` protocol for more information.
66 | */
67 | @property (weak) IBOutlet id delegate;
68 |
69 | /**
70 | * Defines, which edges should be refreshable.
71 | * To assign multiple edges, simply add them with a bitwise OR operator:
72 | *
73 | * scrollView.refreshableEdges = ITPullToRefreshEdgeTop | ITPullToRefreshEdgeBottom
74 | */
75 | @property (nonatomic) NSUInteger refreshableEdges;
76 |
77 | /**
78 | * A bitwise representation of the triggered edges.
79 | *
80 | * Edges are triggered, if the whole edge view is visible in the scroll view,
81 | * and the scroll gesture did not end yet.
82 | */
83 | @property (readonly) NSUInteger triggeredEdges;
84 |
85 | /**
86 | * A bitwise representation of the refreshing edges.
87 | */
88 | @property (readonly) NSUInteger refreshingEdges;
89 |
90 | /**
91 | * Get's the edge view for an edge
92 | *
93 | * @param edge - The edge for which the edge view should be returned
94 | *
95 | * @return The edge view for a specific edge
96 | */
97 | - (ITPullToRefreshEdgeView *)edgeViewForEdge:(ITPullToRefreshEdge)edge;
98 |
99 | /**
100 | * Should be invoked by the delegate, when the refresh action is done.
101 | *
102 | * @param edge - The edge that should stop refreshing
103 | */
104 | - (void)stopRefreshingEdge:(ITPullToRefreshEdge)edge;
105 |
106 | @end
107 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/Classes/ITPullToRefreshScrollView.m:
--------------------------------------------------------------------------------
1 | //
2 | // ITPullToRefreshScrollView.m
3 | // ITPullToRefreshScrollView
4 | //
5 | // Created by Ilija Tovilo on 9/25/13.
6 | // Copyright (c) 2013 Ilija Tovilo. All rights reserved.
7 | //
8 |
9 | #import "ITPullToRefreshScrollView.h"
10 | #import
11 | #import "ITPullToRefreshEdgeView.h"
12 | #import "ITPullToRefreshClipView.h"
13 | #import "DuxScrollViewAnimation.h"
14 |
15 | void dispatch_sync_on_main(dispatch_block_t block);
16 | void dispatch_sync_on_main(dispatch_block_t block) {
17 | if ([NSThread isMainThread]) {
18 | block();
19 | } else {
20 | dispatch_sync(dispatch_get_main_queue(), block);
21 | }
22 | }
23 |
24 |
25 | @interface ITPullToRefreshScrollView () {
26 | BOOL _isLocked;
27 | NSUInteger _edgesToBeRemoved;
28 | NSMutableDictionary *_edgeViews;
29 | }
30 | @end
31 |
32 |
33 | @implementation ITPullToRefreshScrollView
34 |
35 | #pragma mark - Init
36 |
37 | - (id)initWithCoder:(NSCoder *)coder
38 | {
39 | self = [super initWithCoder:coder];
40 | if (self) {
41 | [self initialSetup];
42 | }
43 | return self;
44 | }
45 |
46 | - (id)initWithFrame:(NSRect)frame
47 | {
48 | self = [super initWithFrame:frame];
49 | if (self) {
50 | [self initialSetup];
51 | }
52 | return self;
53 | }
54 |
55 | - (void)initialSetup {
56 | _edgeViews = [NSMutableDictionary dictionary];
57 | [self installCustomClipView];
58 |
59 | self.contentView.postsBoundsChangedNotifications = YES;
60 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boundsDidChange:) name:NSViewBoundsDidChangeNotification object:self.contentView];
61 | }
62 |
63 | - (IBAction)boundsDidChange:(NSNotification *)notification {
64 | [self scrollingChangedWithEvent:nil];
65 | }
66 |
67 | - (void)installCustomClipView {
68 | if ([self.contentView isKindOfClass:[ITPullToRefreshClipView class]])
69 | return;
70 |
71 | ITPullToRefreshClipView *newClipView = [[ITPullToRefreshClipView alloc] initWithFrame:NSZeroRect];
72 |
73 | newClipView.documentView = self.contentView.documentView;
74 | self.contentView = newClipView;
75 |
76 | newClipView.translatesAutoresizingMaskIntoConstraints = NO;
77 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
78 | @"H:|[view]|" options:0 metrics:nil views:@{@"view": newClipView}]];
79 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
80 | @"V:|[view]|" options:0 metrics:nil views:@{@"view": newClipView}]];
81 | }
82 |
83 |
84 | #pragma mark - NSResponder
85 |
86 | - (void)scrollingChangedWithEvent:(NSEvent *)theEvent {
87 | if (_isLocked) return;
88 |
89 | void (^scrollBlock)(ITPullToRefreshEdge edge, CGFloat (^progressBlock)(void)) =
90 | ^(ITPullToRefreshEdge edge, CGFloat (^progressBlock)()) {
91 | if (!(_refreshingEdges & edge)) {
92 | ITPullToRefreshEdgeView *edgeView = [self edgeViewForEdge:edge];
93 | CGFloat progress = progressBlock();
94 |
95 | if ((edge & self.refreshableEdges) && progress > 0) {
96 | [edgeView pullToRefreshScrollView:self didScrollWithProgress:progress];
97 |
98 | if (progress >= 1.0) {
99 | if (!(self.triggeredEdges & edge)) {
100 | [edgeView pullToRefreshScrollViewDidTriggerRefresh:self];
101 | }
102 |
103 | _triggeredEdges |= edge;
104 | } else {
105 | if (self.triggeredEdges & edge) {
106 | [edgeView pullToRefreshScrollViewDidUntriggerRefresh:self];
107 | }
108 |
109 | _triggeredEdges &= ~edge;
110 | }
111 | }
112 | }
113 | };
114 |
115 | // Update edges
116 | if ((ITPullToRefreshEdgeTop & self.refreshableEdges)) {
117 | scrollBlock(ITPullToRefreshEdgeTop, ^{
118 | ITPullToRefreshEdgeView *edgeView = [self edgeViewForEdge:ITPullToRefreshEdgeTop];
119 | return (1.0 /
120 | edgeView.frame.size.height *
121 | -self.contentView.bounds.origin.y);
122 | });
123 | }
124 | if ((ITPullToRefreshEdgeBottom & self.refreshableEdges)) {
125 | scrollBlock(ITPullToRefreshEdgeBottom, ^{
126 | ITPullToRefreshEdgeView *edgeView = [self edgeViewForEdge:ITPullToRefreshEdgeBottom];
127 |
128 | return (1.0 /
129 | edgeView.bounds.size.height *
130 | -(edgeView.frame.origin.y - (self.contentView.bounds.origin.y + self.contentView.bounds.size.height)));
131 | });
132 | }
133 | }
134 |
135 | - (void)scrollingEndedWithEvent:(NSEvent *)theEvent {
136 | if (_isLocked) return;
137 |
138 | [self enumerateThroughEdges:^(ITPullToRefreshEdge edge) {
139 | if (_triggeredEdges & edge) {
140 | _triggeredEdges &= ~edge;
141 | _refreshingEdges |= edge;
142 |
143 | [[self edgeViewForEdge:edge] pullToRefreshScrollViewDidStartRefreshing:self];
144 |
145 | if ([self.delegate respondsToSelector:@selector(pullToRefreshView:didStartRefreshingEdge:)]) {
146 | [self.delegate pullToRefreshView:self didStartRefreshingEdge:edge];
147 | }
148 | }
149 | }];
150 | }
151 |
152 | - (void)scrollWheel:(NSEvent *)theEvent {
153 | if (_isLocked) return;
154 |
155 | const NSEventPhase eventPhase = theEvent.phase;
156 |
157 | if (eventPhase & NSEventPhaseChanged) {
158 | [self scrollingChangedWithEvent:theEvent];
159 | } else if(eventPhase & NSEventPhaseEnded) {
160 | [self scrollingEndedWithEvent:theEvent];
161 | }
162 |
163 | [super scrollWheel:theEvent];
164 | }
165 |
166 | - (void)stopRefreshingEdge:(ITPullToRefreshEdge)edge {
167 | dispatch_sync_on_main(^{
168 | [[self edgeViewForEdge:edge] pullToRefreshScrollViewDidStopRefreshing:self];
169 |
170 | if (_refreshingEdges & edge) {
171 | _isLocked = YES;
172 |
173 | NSPoint scrollPoint = self.contentView.bounds.origin;
174 | NSRect cvb = self.contentView.bounds;
175 | NSRect evf = [self edgeViewForEdge:edge].frame;
176 |
177 | if (edge == ITPullToRefreshEdgeTop) {
178 | if (cvb.origin.y < 0) {
179 | scrollPoint.y += -cvb.origin.y;
180 | } else {
181 | scrollPoint.y += [self edgeViewForEdge:edge].frame.size.height;
182 | }
183 | } else if (edge == ITPullToRefreshEdgeBottom) {
184 | if (cvb.origin.y + cvb.size.height > evf.origin.y) {
185 | scrollPoint.y -= -(evf.origin.y - (cvb.origin.y + cvb.size.height));
186 | } else {
187 | scrollPoint.y -= evf.size.height;
188 | }
189 | }
190 |
191 | _edgesToBeRemoved |= edge;
192 | [DuxScrollViewAnimation animatedScrollToPoint:scrollPoint
193 | inScrollView:self
194 | delegate:self];
195 | }
196 | });
197 | }
198 |
199 | - (void)animationDidEnd:(NSAnimation *)animation {
200 | dispatch_sync_on_main(^{
201 | [self enumerateThroughEdges:^(ITPullToRefreshEdge edge) {
202 | if (_edgesToBeRemoved & edge) {
203 | _edgesToBeRemoved &= ~edge;
204 | _refreshingEdges &= ~edge;
205 |
206 | [self imitateScrollingEventForEdge:edge];
207 |
208 | [[self edgeViewForEdge:edge] pullToRefreshScrollViewDidStopAnimating:self];
209 |
210 | if ([self.delegate respondsToSelector:@selector(pullToRefreshView:didStopRefreshingEdge:)]) {
211 | [self.delegate pullToRefreshView:self didStopRefreshingEdge:edge];
212 | }
213 | }
214 | }];
215 |
216 | if (!_edgesToBeRemoved) _isLocked = NO;
217 | });
218 | }
219 |
220 | - (void)imitateScrollingEventForEdge:(ITPullToRefreshEdge)edge {
221 | NSInteger amount = 0;
222 |
223 | if (edge == ITPullToRefreshEdgeTop) amount = 1;
224 | else if (edge == ITPullToRefreshEdgeBottom) amount = -1;
225 |
226 | CGEventRef cgEvent = CGEventCreateScrollWheelEvent(NULL,
227 | kCGScrollEventUnitLine,
228 | 1,
229 | (int)amount,
230 | 0);
231 |
232 | NSEvent *scrollEvent = [NSEvent eventWithCGEvent:cgEvent];
233 | [self scrollWheel:scrollEvent];
234 | CFRelease(cgEvent);
235 | }
236 |
237 |
238 | #pragma mark - ITPullToRefreshScrollView Methods
239 |
240 | + (Class)edgeViewClassForEdge:(ITPullToRefreshEdge)edge {
241 | return [ITPullToRefreshEdgeView class];
242 | }
243 |
244 |
245 | #pragma mark - Properties
246 |
247 | - (ITPullToRefreshEdgeView *)edgeViewForEdge:(ITPullToRefreshEdge)edge {
248 | if (!_edgeViews[@( edge )]) {
249 | [_edgeViews setObject:[[[[self class] edgeViewClassForEdge:ITPullToRefreshEdgeBottom] alloc] initWithEdge:edge]
250 | forKey:@( edge )];
251 | }
252 |
253 | return _edgeViews[@( edge )];
254 | }
255 |
256 | - (void)setRefreshableEdges:(NSUInteger)refreshableEdges {
257 | _refreshableEdges = refreshableEdges;
258 |
259 | [self enumerateThroughEdges:^(ITPullToRefreshEdge edge) {
260 | if (_refreshableEdges & edge)
261 | {
262 | ITPullToRefreshEdgeView *edgeView = [self edgeViewForEdge:edge];
263 |
264 | [self.contentView addSubview:edgeView];
265 | }
266 | else
267 | {
268 | [[self edgeViewForEdge:edge] removeFromSuperview];
269 | }
270 | }];
271 | }
272 |
273 | - (void)enumerateThroughEdges:(void (^)(ITPullToRefreshEdge))block {
274 | for (ITPullToRefreshEdge edge = ITPullToRefreshEdgeTop; edge <= ITPullToRefreshEdgeBottom; edge++) {
275 | block(edge);
276 | }
277 | }
278 |
279 | @end
280 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 91915E3917F98453002031B0 /* NSBKeyframeAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 91915E3617F98453002031B0 /* NSBKeyframeAnimation.m */; };
11 | 91915E3A17F98453002031B0 /* NSBKeyframeAnimationFunctions.c in Sources */ = {isa = PBXBuildFile; fileRef = 91915E3717F98453002031B0 /* NSBKeyframeAnimationFunctions.c */; };
12 | 91915E3F17F984A1002031B0 /* ITProgressIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 91915E3C17F984A1002031B0 /* ITProgressIndicator.m */; };
13 | 91EE6BFC17FAC2F9004A307A /* DuxScrollViewAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 91EE6BFB17FAC2F9004A307A /* DuxScrollViewAnimation.m */; };
14 | 91F1EF0D17F2D4FD00B5017E /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91F1EF0C17F2D4FD00B5017E /* Cocoa.framework */; };
15 | 91F1EF1717F2D4FD00B5017E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 91F1EF1517F2D4FD00B5017E /* InfoPlist.strings */; };
16 | 91F1EF1917F2D4FD00B5017E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 91F1EF1817F2D4FD00B5017E /* main.m */; };
17 | 91F1EF1D17F2D4FD00B5017E /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 91F1EF1B17F2D4FD00B5017E /* Credits.rtf */; };
18 | 91F1EF2017F2D4FD00B5017E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 91F1EF1F17F2D4FD00B5017E /* AppDelegate.m */; };
19 | 91F1EF2317F2D4FD00B5017E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 91F1EF2117F2D4FD00B5017E /* MainMenu.xib */; };
20 | 91F1EF2517F2D4FD00B5017E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 91F1EF2417F2D4FD00B5017E /* Images.xcassets */; };
21 | 91F1EF2C17F2D4FD00B5017E /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91F1EF2B17F2D4FD00B5017E /* XCTest.framework */; };
22 | 91F1EF2D17F2D4FD00B5017E /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91F1EF0C17F2D4FD00B5017E /* Cocoa.framework */; };
23 | 91F1EF3517F2D4FD00B5017E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 91F1EF3317F2D4FD00B5017E /* InfoPlist.strings */; };
24 | 91F1EF3717F2D4FD00B5017E /* ITPullToRefreshScrollViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 91F1EF3617F2D4FD00B5017E /* ITPullToRefreshScrollViewTests.m */; };
25 | 91F1EF4C17F2DAE900B5017E /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91F1EF4B17F2DAE900B5017E /* QuartzCore.framework */; };
26 | 91F1EF6117F2FD0C00B5017E /* ITPullToRefreshClipView.m in Sources */ = {isa = PBXBuildFile; fileRef = 91F1EF5C17F2FD0C00B5017E /* ITPullToRefreshClipView.m */; };
27 | 91F1EF6217F2FD0C00B5017E /* ITPullToRefreshEdgeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 91F1EF5E17F2FD0C00B5017E /* ITPullToRefreshEdgeView.m */; };
28 | 91F1EF6317F2FD0C00B5017E /* ITPullToRefreshScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 91F1EF6017F2FD0C00B5017E /* ITPullToRefreshScrollView.m */; };
29 | /* End PBXBuildFile section */
30 |
31 | /* Begin PBXContainerItemProxy section */
32 | 91F1EF2E17F2D4FD00B5017E /* PBXContainerItemProxy */ = {
33 | isa = PBXContainerItemProxy;
34 | containerPortal = 91F1EF0117F2D4FD00B5017E /* Project object */;
35 | proxyType = 1;
36 | remoteGlobalIDString = 91F1EF0817F2D4FD00B5017E;
37 | remoteInfo = ITPullToRefreshScrollView;
38 | };
39 | /* End PBXContainerItemProxy section */
40 |
41 | /* Begin PBXFileReference section */
42 | 91915E3517F98453002031B0 /* NSBKeyframeAnimation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSBKeyframeAnimation.h; path = ../../Modules/NSBKeyframeAnimation/NSBKeyframeAnimation/Classes/NSBKeyframeAnimation/NSBKeyframeAnimation.h; sourceTree = ""; };
43 | 91915E3617F98453002031B0 /* NSBKeyframeAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSBKeyframeAnimation.m; path = ../../Modules/NSBKeyframeAnimation/NSBKeyframeAnimation/Classes/NSBKeyframeAnimation/NSBKeyframeAnimation.m; sourceTree = ""; };
44 | 91915E3717F98453002031B0 /* NSBKeyframeAnimationFunctions.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = NSBKeyframeAnimationFunctions.c; path = ../../Modules/NSBKeyframeAnimation/NSBKeyframeAnimation/Classes/NSBKeyframeAnimation/NSBKeyframeAnimationFunctions.c; sourceTree = ""; };
45 | 91915E3817F98453002031B0 /* NSBKeyframeAnimationFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSBKeyframeAnimationFunctions.h; path = ../../Modules/NSBKeyframeAnimation/NSBKeyframeAnimation/Classes/NSBKeyframeAnimation/NSBKeyframeAnimationFunctions.h; sourceTree = ""; };
46 | 91915E3B17F984A1002031B0 /* ITProgressIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ITProgressIndicator.h; path = ../../Modules/ITProgressIndicator/ITProgressIndicator/Classes/ITProgressIndicator.h; sourceTree = ""; };
47 | 91915E3C17F984A1002031B0 /* ITProgressIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ITProgressIndicator.m; path = ../../Modules/ITProgressIndicator/ITProgressIndicator/Classes/ITProgressIndicator.m; sourceTree = ""; };
48 | 91EE6BFA17FAC2F9004A307A /* DuxScrollViewAnimation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DuxScrollViewAnimation.h; path = Classes/DuxScrollViewAnimation.h; sourceTree = SOURCE_ROOT; };
49 | 91EE6BFB17FAC2F9004A307A /* DuxScrollViewAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DuxScrollViewAnimation.m; path = Classes/DuxScrollViewAnimation.m; sourceTree = SOURCE_ROOT; };
50 | 91F1EF0917F2D4FD00B5017E /* ITPullToRefreshScrollView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ITPullToRefreshScrollView.app; sourceTree = BUILT_PRODUCTS_DIR; };
51 | 91F1EF0C17F2D4FD00B5017E /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
52 | 91F1EF0F17F2D4FD00B5017E /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
53 | 91F1EF1017F2D4FD00B5017E /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
54 | 91F1EF1117F2D4FD00B5017E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
55 | 91F1EF1417F2D4FD00B5017E /* ITPullToRefreshScrollView-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ITPullToRefreshScrollView-Info.plist"; sourceTree = ""; };
56 | 91F1EF1617F2D4FD00B5017E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
57 | 91F1EF1817F2D4FD00B5017E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
58 | 91F1EF1A17F2D4FD00B5017E /* ITPullToRefreshScrollView-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ITPullToRefreshScrollView-Prefix.pch"; sourceTree = ""; };
59 | 91F1EF1C17F2D4FD00B5017E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; };
60 | 91F1EF1E17F2D4FD00B5017E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
61 | 91F1EF1F17F2D4FD00B5017E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
62 | 91F1EF2217F2D4FD00B5017E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
63 | 91F1EF2417F2D4FD00B5017E /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
64 | 91F1EF2A17F2D4FD00B5017E /* ITPullToRefreshScrollViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ITPullToRefreshScrollViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
65 | 91F1EF2B17F2D4FD00B5017E /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
66 | 91F1EF3217F2D4FD00B5017E /* ITPullToRefreshScrollViewTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ITPullToRefreshScrollViewTests-Info.plist"; sourceTree = ""; };
67 | 91F1EF3417F2D4FD00B5017E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
68 | 91F1EF3617F2D4FD00B5017E /* ITPullToRefreshScrollViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ITPullToRefreshScrollViewTests.m; sourceTree = ""; };
69 | 91F1EF4B17F2DAE900B5017E /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
70 | 91F1EF5B17F2FD0C00B5017E /* ITPullToRefreshClipView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ITPullToRefreshClipView.h; path = Classes/ITPullToRefreshClipView.h; sourceTree = SOURCE_ROOT; };
71 | 91F1EF5C17F2FD0C00B5017E /* ITPullToRefreshClipView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ITPullToRefreshClipView.m; path = Classes/ITPullToRefreshClipView.m; sourceTree = SOURCE_ROOT; };
72 | 91F1EF5D17F2FD0C00B5017E /* ITPullToRefreshEdgeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ITPullToRefreshEdgeView.h; path = Classes/ITPullToRefreshEdgeView.h; sourceTree = SOURCE_ROOT; };
73 | 91F1EF5E17F2FD0C00B5017E /* ITPullToRefreshEdgeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ITPullToRefreshEdgeView.m; path = Classes/ITPullToRefreshEdgeView.m; sourceTree = SOURCE_ROOT; };
74 | 91F1EF5F17F2FD0C00B5017E /* ITPullToRefreshScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ITPullToRefreshScrollView.h; path = Classes/ITPullToRefreshScrollView.h; sourceTree = SOURCE_ROOT; };
75 | 91F1EF6017F2FD0C00B5017E /* ITPullToRefreshScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ITPullToRefreshScrollView.m; path = Classes/ITPullToRefreshScrollView.m; sourceTree = SOURCE_ROOT; };
76 | /* End PBXFileReference section */
77 |
78 | /* Begin PBXFrameworksBuildPhase section */
79 | 91F1EF0617F2D4FD00B5017E /* Frameworks */ = {
80 | isa = PBXFrameworksBuildPhase;
81 | buildActionMask = 2147483647;
82 | files = (
83 | 91F1EF4C17F2DAE900B5017E /* QuartzCore.framework in Frameworks */,
84 | 91F1EF0D17F2D4FD00B5017E /* Cocoa.framework in Frameworks */,
85 | );
86 | runOnlyForDeploymentPostprocessing = 0;
87 | };
88 | 91F1EF2717F2D4FD00B5017E /* Frameworks */ = {
89 | isa = PBXFrameworksBuildPhase;
90 | buildActionMask = 2147483647;
91 | files = (
92 | 91F1EF2D17F2D4FD00B5017E /* Cocoa.framework in Frameworks */,
93 | 91F1EF2C17F2D4FD00B5017E /* XCTest.framework in Frameworks */,
94 | );
95 | runOnlyForDeploymentPostprocessing = 0;
96 | };
97 | /* End PBXFrameworksBuildPhase section */
98 |
99 | /* Begin PBXGroup section */
100 | 912A2D7817F965CB00EB403A /* NSBKeyframeAnimation */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 91915E3517F98453002031B0 /* NSBKeyframeAnimation.h */,
104 | 91915E3617F98453002031B0 /* NSBKeyframeAnimation.m */,
105 | 91915E3717F98453002031B0 /* NSBKeyframeAnimationFunctions.c */,
106 | 91915E3817F98453002031B0 /* NSBKeyframeAnimationFunctions.h */,
107 | );
108 | name = NSBKeyframeAnimation;
109 | sourceTree = "";
110 | };
111 | 91DA1D0017F895810076E848 /* Scroll Animation */ = {
112 | isa = PBXGroup;
113 | children = (
114 | 91EE6BFA17FAC2F9004A307A /* DuxScrollViewAnimation.h */,
115 | 91EE6BFB17FAC2F9004A307A /* DuxScrollViewAnimation.m */,
116 | );
117 | name = "Scroll Animation";
118 | sourceTree = "";
119 | };
120 | 91DA1D0717F8A93D0076E848 /* ITProgressIndicator */ = {
121 | isa = PBXGroup;
122 | children = (
123 | 91915E3B17F984A1002031B0 /* ITProgressIndicator.h */,
124 | 91915E3C17F984A1002031B0 /* ITProgressIndicator.m */,
125 | );
126 | name = ITProgressIndicator;
127 | sourceTree = "";
128 | };
129 | 91F1EF0017F2D4FD00B5017E = {
130 | isa = PBXGroup;
131 | children = (
132 | 91F1EF1217F2D4FD00B5017E /* ITPullToRefreshScrollView */,
133 | 91F1EF3017F2D4FD00B5017E /* ITPullToRefreshScrollViewTests */,
134 | 91F1EF0B17F2D4FD00B5017E /* Frameworks */,
135 | 91F1EF0A17F2D4FD00B5017E /* Products */,
136 | );
137 | sourceTree = "";
138 | };
139 | 91F1EF0A17F2D4FD00B5017E /* Products */ = {
140 | isa = PBXGroup;
141 | children = (
142 | 91F1EF0917F2D4FD00B5017E /* ITPullToRefreshScrollView.app */,
143 | 91F1EF2A17F2D4FD00B5017E /* ITPullToRefreshScrollViewTests.xctest */,
144 | );
145 | name = Products;
146 | sourceTree = "";
147 | };
148 | 91F1EF0B17F2D4FD00B5017E /* Frameworks */ = {
149 | isa = PBXGroup;
150 | children = (
151 | 91F1EF4B17F2DAE900B5017E /* QuartzCore.framework */,
152 | 91F1EF0C17F2D4FD00B5017E /* Cocoa.framework */,
153 | 91F1EF2B17F2D4FD00B5017E /* XCTest.framework */,
154 | 91F1EF0E17F2D4FD00B5017E /* Other Frameworks */,
155 | );
156 | name = Frameworks;
157 | sourceTree = "";
158 | };
159 | 91F1EF0E17F2D4FD00B5017E /* Other Frameworks */ = {
160 | isa = PBXGroup;
161 | children = (
162 | 91F1EF0F17F2D4FD00B5017E /* AppKit.framework */,
163 | 91F1EF1017F2D4FD00B5017E /* CoreData.framework */,
164 | 91F1EF1117F2D4FD00B5017E /* Foundation.framework */,
165 | );
166 | name = "Other Frameworks";
167 | sourceTree = "";
168 | };
169 | 91F1EF1217F2D4FD00B5017E /* ITPullToRefreshScrollView */ = {
170 | isa = PBXGroup;
171 | children = (
172 | 91F1EF1E17F2D4FD00B5017E /* AppDelegate.h */,
173 | 91F1EF1F17F2D4FD00B5017E /* AppDelegate.m */,
174 | 91F1EF5317F2F32A00B5017E /* Classes */,
175 | 91DA1D0717F8A93D0076E848 /* ITProgressIndicator */,
176 | 912A2D7817F965CB00EB403A /* NSBKeyframeAnimation */,
177 | 91DA1D0017F895810076E848 /* Scroll Animation */,
178 | 91F1EF2117F2D4FD00B5017E /* MainMenu.xib */,
179 | 91F1EF2417F2D4FD00B5017E /* Images.xcassets */,
180 | 91F1EF1317F2D4FD00B5017E /* Supporting Files */,
181 | );
182 | path = ITPullToRefreshScrollView;
183 | sourceTree = "";
184 | };
185 | 91F1EF1317F2D4FD00B5017E /* Supporting Files */ = {
186 | isa = PBXGroup;
187 | children = (
188 | 91F1EF1417F2D4FD00B5017E /* ITPullToRefreshScrollView-Info.plist */,
189 | 91F1EF1517F2D4FD00B5017E /* InfoPlist.strings */,
190 | 91F1EF1817F2D4FD00B5017E /* main.m */,
191 | 91F1EF1A17F2D4FD00B5017E /* ITPullToRefreshScrollView-Prefix.pch */,
192 | 91F1EF1B17F2D4FD00B5017E /* Credits.rtf */,
193 | );
194 | name = "Supporting Files";
195 | sourceTree = "";
196 | };
197 | 91F1EF3017F2D4FD00B5017E /* ITPullToRefreshScrollViewTests */ = {
198 | isa = PBXGroup;
199 | children = (
200 | 91F1EF3617F2D4FD00B5017E /* ITPullToRefreshScrollViewTests.m */,
201 | 91F1EF3117F2D4FD00B5017E /* Supporting Files */,
202 | );
203 | path = ITPullToRefreshScrollViewTests;
204 | sourceTree = "";
205 | };
206 | 91F1EF3117F2D4FD00B5017E /* Supporting Files */ = {
207 | isa = PBXGroup;
208 | children = (
209 | 91F1EF3217F2D4FD00B5017E /* ITPullToRefreshScrollViewTests-Info.plist */,
210 | 91F1EF3317F2D4FD00B5017E /* InfoPlist.strings */,
211 | );
212 | name = "Supporting Files";
213 | sourceTree = "";
214 | };
215 | 91F1EF5317F2F32A00B5017E /* Classes */ = {
216 | isa = PBXGroup;
217 | children = (
218 | 91F1EF5F17F2FD0C00B5017E /* ITPullToRefreshScrollView.h */,
219 | 91F1EF6017F2FD0C00B5017E /* ITPullToRefreshScrollView.m */,
220 | 91F1EF5B17F2FD0C00B5017E /* ITPullToRefreshClipView.h */,
221 | 91F1EF5C17F2FD0C00B5017E /* ITPullToRefreshClipView.m */,
222 | 91F1EF5D17F2FD0C00B5017E /* ITPullToRefreshEdgeView.h */,
223 | 91F1EF5E17F2FD0C00B5017E /* ITPullToRefreshEdgeView.m */,
224 | );
225 | name = Classes;
226 | sourceTree = "";
227 | };
228 | /* End PBXGroup section */
229 |
230 | /* Begin PBXNativeTarget section */
231 | 91F1EF0817F2D4FD00B5017E /* ITPullToRefreshScrollView */ = {
232 | isa = PBXNativeTarget;
233 | buildConfigurationList = 91F1EF3A17F2D4FD00B5017E /* Build configuration list for PBXNativeTarget "ITPullToRefreshScrollView" */;
234 | buildPhases = (
235 | 91F1EF0517F2D4FD00B5017E /* Sources */,
236 | 91F1EF0617F2D4FD00B5017E /* Frameworks */,
237 | 91F1EF0717F2D4FD00B5017E /* Resources */,
238 | );
239 | buildRules = (
240 | );
241 | dependencies = (
242 | );
243 | name = ITPullToRefreshScrollView;
244 | productName = ITPullToRefreshScrollView;
245 | productReference = 91F1EF0917F2D4FD00B5017E /* ITPullToRefreshScrollView.app */;
246 | productType = "com.apple.product-type.application";
247 | };
248 | 91F1EF2917F2D4FD00B5017E /* ITPullToRefreshScrollViewTests */ = {
249 | isa = PBXNativeTarget;
250 | buildConfigurationList = 91F1EF3D17F2D4FD00B5017E /* Build configuration list for PBXNativeTarget "ITPullToRefreshScrollViewTests" */;
251 | buildPhases = (
252 | 91F1EF2617F2D4FD00B5017E /* Sources */,
253 | 91F1EF2717F2D4FD00B5017E /* Frameworks */,
254 | 91F1EF2817F2D4FD00B5017E /* Resources */,
255 | );
256 | buildRules = (
257 | );
258 | dependencies = (
259 | 91F1EF2F17F2D4FD00B5017E /* PBXTargetDependency */,
260 | );
261 | name = ITPullToRefreshScrollViewTests;
262 | productName = ITPullToRefreshScrollViewTests;
263 | productReference = 91F1EF2A17F2D4FD00B5017E /* ITPullToRefreshScrollViewTests.xctest */;
264 | productType = "com.apple.product-type.bundle.unit-test";
265 | };
266 | /* End PBXNativeTarget section */
267 |
268 | /* Begin PBXProject section */
269 | 91F1EF0117F2D4FD00B5017E /* Project object */ = {
270 | isa = PBXProject;
271 | attributes = {
272 | LastUpgradeCheck = 0820;
273 | ORGANIZATIONNAME = "Ilija Tovilo";
274 | TargetAttributes = {
275 | 91F1EF2917F2D4FD00B5017E = {
276 | TestTargetID = 91F1EF0817F2D4FD00B5017E;
277 | };
278 | };
279 | };
280 | buildConfigurationList = 91F1EF0417F2D4FD00B5017E /* Build configuration list for PBXProject "ITPullToRefreshScrollView" */;
281 | compatibilityVersion = "Xcode 3.2";
282 | developmentRegion = English;
283 | hasScannedForEncodings = 0;
284 | knownRegions = (
285 | en,
286 | Base,
287 | );
288 | mainGroup = 91F1EF0017F2D4FD00B5017E;
289 | productRefGroup = 91F1EF0A17F2D4FD00B5017E /* Products */;
290 | projectDirPath = "";
291 | projectRoot = "";
292 | targets = (
293 | 91F1EF0817F2D4FD00B5017E /* ITPullToRefreshScrollView */,
294 | 91F1EF2917F2D4FD00B5017E /* ITPullToRefreshScrollViewTests */,
295 | );
296 | };
297 | /* End PBXProject section */
298 |
299 | /* Begin PBXResourcesBuildPhase section */
300 | 91F1EF0717F2D4FD00B5017E /* Resources */ = {
301 | isa = PBXResourcesBuildPhase;
302 | buildActionMask = 2147483647;
303 | files = (
304 | 91F1EF1717F2D4FD00B5017E /* InfoPlist.strings in Resources */,
305 | 91F1EF2517F2D4FD00B5017E /* Images.xcassets in Resources */,
306 | 91F1EF1D17F2D4FD00B5017E /* Credits.rtf in Resources */,
307 | 91F1EF2317F2D4FD00B5017E /* MainMenu.xib in Resources */,
308 | );
309 | runOnlyForDeploymentPostprocessing = 0;
310 | };
311 | 91F1EF2817F2D4FD00B5017E /* Resources */ = {
312 | isa = PBXResourcesBuildPhase;
313 | buildActionMask = 2147483647;
314 | files = (
315 | 91F1EF3517F2D4FD00B5017E /* InfoPlist.strings in Resources */,
316 | );
317 | runOnlyForDeploymentPostprocessing = 0;
318 | };
319 | /* End PBXResourcesBuildPhase section */
320 |
321 | /* Begin PBXSourcesBuildPhase section */
322 | 91F1EF0517F2D4FD00B5017E /* Sources */ = {
323 | isa = PBXSourcesBuildPhase;
324 | buildActionMask = 2147483647;
325 | files = (
326 | 91915E3917F98453002031B0 /* NSBKeyframeAnimation.m in Sources */,
327 | 91915E3A17F98453002031B0 /* NSBKeyframeAnimationFunctions.c in Sources */,
328 | 91F1EF6217F2FD0C00B5017E /* ITPullToRefreshEdgeView.m in Sources */,
329 | 91F1EF2017F2D4FD00B5017E /* AppDelegate.m in Sources */,
330 | 91F1EF1917F2D4FD00B5017E /* main.m in Sources */,
331 | 91915E3F17F984A1002031B0 /* ITProgressIndicator.m in Sources */,
332 | 91F1EF6317F2FD0C00B5017E /* ITPullToRefreshScrollView.m in Sources */,
333 | 91EE6BFC17FAC2F9004A307A /* DuxScrollViewAnimation.m in Sources */,
334 | 91F1EF6117F2FD0C00B5017E /* ITPullToRefreshClipView.m in Sources */,
335 | );
336 | runOnlyForDeploymentPostprocessing = 0;
337 | };
338 | 91F1EF2617F2D4FD00B5017E /* Sources */ = {
339 | isa = PBXSourcesBuildPhase;
340 | buildActionMask = 2147483647;
341 | files = (
342 | 91F1EF3717F2D4FD00B5017E /* ITPullToRefreshScrollViewTests.m in Sources */,
343 | );
344 | runOnlyForDeploymentPostprocessing = 0;
345 | };
346 | /* End PBXSourcesBuildPhase section */
347 |
348 | /* Begin PBXTargetDependency section */
349 | 91F1EF2F17F2D4FD00B5017E /* PBXTargetDependency */ = {
350 | isa = PBXTargetDependency;
351 | target = 91F1EF0817F2D4FD00B5017E /* ITPullToRefreshScrollView */;
352 | targetProxy = 91F1EF2E17F2D4FD00B5017E /* PBXContainerItemProxy */;
353 | };
354 | /* End PBXTargetDependency section */
355 |
356 | /* Begin PBXVariantGroup section */
357 | 91F1EF1517F2D4FD00B5017E /* InfoPlist.strings */ = {
358 | isa = PBXVariantGroup;
359 | children = (
360 | 91F1EF1617F2D4FD00B5017E /* en */,
361 | );
362 | name = InfoPlist.strings;
363 | sourceTree = "";
364 | };
365 | 91F1EF1B17F2D4FD00B5017E /* Credits.rtf */ = {
366 | isa = PBXVariantGroup;
367 | children = (
368 | 91F1EF1C17F2D4FD00B5017E /* en */,
369 | );
370 | name = Credits.rtf;
371 | sourceTree = "";
372 | };
373 | 91F1EF2117F2D4FD00B5017E /* MainMenu.xib */ = {
374 | isa = PBXVariantGroup;
375 | children = (
376 | 91F1EF2217F2D4FD00B5017E /* Base */,
377 | );
378 | name = MainMenu.xib;
379 | sourceTree = "";
380 | };
381 | 91F1EF3317F2D4FD00B5017E /* InfoPlist.strings */ = {
382 | isa = PBXVariantGroup;
383 | children = (
384 | 91F1EF3417F2D4FD00B5017E /* en */,
385 | );
386 | name = InfoPlist.strings;
387 | sourceTree = "";
388 | };
389 | /* End PBXVariantGroup section */
390 |
391 | /* Begin XCBuildConfiguration section */
392 | 91F1EF3817F2D4FD00B5017E /* Debug */ = {
393 | isa = XCBuildConfiguration;
394 | buildSettings = {
395 | ALWAYS_SEARCH_USER_PATHS = NO;
396 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
397 | CLANG_CXX_LIBRARY = "libc++";
398 | CLANG_ENABLE_OBJC_ARC = YES;
399 | CLANG_WARN_BOOL_CONVERSION = YES;
400 | CLANG_WARN_CONSTANT_CONVERSION = YES;
401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
402 | CLANG_WARN_EMPTY_BODY = YES;
403 | CLANG_WARN_ENUM_CONVERSION = YES;
404 | CLANG_WARN_INFINITE_RECURSION = YES;
405 | CLANG_WARN_INT_CONVERSION = YES;
406 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
407 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
408 | CLANG_WARN_UNREACHABLE_CODE = YES;
409 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
410 | COPY_PHASE_STRIP = NO;
411 | ENABLE_STRICT_OBJC_MSGSEND = YES;
412 | ENABLE_TESTABILITY = YES;
413 | GCC_C_LANGUAGE_STANDARD = gnu99;
414 | GCC_DYNAMIC_NO_PIC = NO;
415 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
416 | GCC_NO_COMMON_BLOCKS = YES;
417 | GCC_OPTIMIZATION_LEVEL = 0;
418 | GCC_PREPROCESSOR_DEFINITIONS = (
419 | "DEBUG=1",
420 | "$(inherited)",
421 | );
422 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
423 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
424 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
425 | GCC_WARN_UNDECLARED_SELECTOR = YES;
426 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
427 | GCC_WARN_UNUSED_FUNCTION = YES;
428 | GCC_WARN_UNUSED_VARIABLE = YES;
429 | MACOSX_DEPLOYMENT_TARGET = 10.8;
430 | ONLY_ACTIVE_ARCH = YES;
431 | SDKROOT = macosx;
432 | };
433 | name = Debug;
434 | };
435 | 91F1EF3917F2D4FD00B5017E /* Release */ = {
436 | isa = XCBuildConfiguration;
437 | buildSettings = {
438 | ALWAYS_SEARCH_USER_PATHS = NO;
439 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
440 | CLANG_CXX_LIBRARY = "libc++";
441 | CLANG_ENABLE_OBJC_ARC = YES;
442 | CLANG_WARN_BOOL_CONVERSION = YES;
443 | CLANG_WARN_CONSTANT_CONVERSION = YES;
444 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
445 | CLANG_WARN_EMPTY_BODY = YES;
446 | CLANG_WARN_ENUM_CONVERSION = YES;
447 | CLANG_WARN_INFINITE_RECURSION = YES;
448 | CLANG_WARN_INT_CONVERSION = YES;
449 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
450 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
451 | CLANG_WARN_UNREACHABLE_CODE = YES;
452 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
453 | COPY_PHASE_STRIP = YES;
454 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
455 | ENABLE_NS_ASSERTIONS = NO;
456 | ENABLE_STRICT_OBJC_MSGSEND = YES;
457 | GCC_C_LANGUAGE_STANDARD = gnu99;
458 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
459 | GCC_NO_COMMON_BLOCKS = YES;
460 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
461 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
462 | GCC_WARN_UNDECLARED_SELECTOR = YES;
463 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
464 | GCC_WARN_UNUSED_FUNCTION = YES;
465 | GCC_WARN_UNUSED_VARIABLE = YES;
466 | MACOSX_DEPLOYMENT_TARGET = 10.8;
467 | SDKROOT = macosx;
468 | };
469 | name = Release;
470 | };
471 | 91F1EF3B17F2D4FD00B5017E /* Debug */ = {
472 | isa = XCBuildConfiguration;
473 | buildSettings = {
474 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
475 | COMBINE_HIDPI_IMAGES = YES;
476 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
477 | GCC_PREFIX_HEADER = "ITPullToRefreshScrollView/ITPullToRefreshScrollView-Prefix.pch";
478 | INFOPLIST_FILE = "ITPullToRefreshScrollView/ITPullToRefreshScrollView-Info.plist";
479 | PRODUCT_BUNDLE_IDENTIFIER = "ch.ilijatovilo.${PRODUCT_NAME:rfc1034identifier}";
480 | PRODUCT_NAME = "$(TARGET_NAME)";
481 | WRAPPER_EXTENSION = app;
482 | };
483 | name = Debug;
484 | };
485 | 91F1EF3C17F2D4FD00B5017E /* Release */ = {
486 | isa = XCBuildConfiguration;
487 | buildSettings = {
488 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
489 | COMBINE_HIDPI_IMAGES = YES;
490 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
491 | GCC_PREFIX_HEADER = "ITPullToRefreshScrollView/ITPullToRefreshScrollView-Prefix.pch";
492 | INFOPLIST_FILE = "ITPullToRefreshScrollView/ITPullToRefreshScrollView-Info.plist";
493 | PRODUCT_BUNDLE_IDENTIFIER = "ch.ilijatovilo.${PRODUCT_NAME:rfc1034identifier}";
494 | PRODUCT_NAME = "$(TARGET_NAME)";
495 | WRAPPER_EXTENSION = app;
496 | };
497 | name = Release;
498 | };
499 | 91F1EF3E17F2D4FD00B5017E /* Debug */ = {
500 | isa = XCBuildConfiguration;
501 | buildSettings = {
502 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ITPullToRefreshScrollView.app/Contents/MacOS/ITPullToRefreshScrollView";
503 | COMBINE_HIDPI_IMAGES = YES;
504 | FRAMEWORK_SEARCH_PATHS = (
505 | "$(DEVELOPER_FRAMEWORKS_DIR)",
506 | "$(inherited)",
507 | );
508 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
509 | GCC_PREFIX_HEADER = "ITPullToRefreshScrollView/ITPullToRefreshScrollView-Prefix.pch";
510 | GCC_PREPROCESSOR_DEFINITIONS = (
511 | "DEBUG=1",
512 | "$(inherited)",
513 | );
514 | INFOPLIST_FILE = "ITPullToRefreshScrollViewTests/ITPullToRefreshScrollViewTests-Info.plist";
515 | PRODUCT_BUNDLE_IDENTIFIER = "ch.ilijatovilo.${PRODUCT_NAME:rfc1034identifier}";
516 | PRODUCT_NAME = "$(TARGET_NAME)";
517 | TEST_HOST = "$(BUNDLE_LOADER)";
518 | WRAPPER_EXTENSION = xctest;
519 | };
520 | name = Debug;
521 | };
522 | 91F1EF3F17F2D4FD00B5017E /* Release */ = {
523 | isa = XCBuildConfiguration;
524 | buildSettings = {
525 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ITPullToRefreshScrollView.app/Contents/MacOS/ITPullToRefreshScrollView";
526 | COMBINE_HIDPI_IMAGES = YES;
527 | FRAMEWORK_SEARCH_PATHS = (
528 | "$(DEVELOPER_FRAMEWORKS_DIR)",
529 | "$(inherited)",
530 | );
531 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
532 | GCC_PREFIX_HEADER = "ITPullToRefreshScrollView/ITPullToRefreshScrollView-Prefix.pch";
533 | INFOPLIST_FILE = "ITPullToRefreshScrollViewTests/ITPullToRefreshScrollViewTests-Info.plist";
534 | PRODUCT_BUNDLE_IDENTIFIER = "ch.ilijatovilo.${PRODUCT_NAME:rfc1034identifier}";
535 | PRODUCT_NAME = "$(TARGET_NAME)";
536 | TEST_HOST = "$(BUNDLE_LOADER)";
537 | WRAPPER_EXTENSION = xctest;
538 | };
539 | name = Release;
540 | };
541 | /* End XCBuildConfiguration section */
542 |
543 | /* Begin XCConfigurationList section */
544 | 91F1EF0417F2D4FD00B5017E /* Build configuration list for PBXProject "ITPullToRefreshScrollView" */ = {
545 | isa = XCConfigurationList;
546 | buildConfigurations = (
547 | 91F1EF3817F2D4FD00B5017E /* Debug */,
548 | 91F1EF3917F2D4FD00B5017E /* Release */,
549 | );
550 | defaultConfigurationIsVisible = 0;
551 | defaultConfigurationName = Release;
552 | };
553 | 91F1EF3A17F2D4FD00B5017E /* Build configuration list for PBXNativeTarget "ITPullToRefreshScrollView" */ = {
554 | isa = XCConfigurationList;
555 | buildConfigurations = (
556 | 91F1EF3B17F2D4FD00B5017E /* Debug */,
557 | 91F1EF3C17F2D4FD00B5017E /* Release */,
558 | );
559 | defaultConfigurationIsVisible = 0;
560 | defaultConfigurationName = Release;
561 | };
562 | 91F1EF3D17F2D4FD00B5017E /* Build configuration list for PBXNativeTarget "ITPullToRefreshScrollViewTests" */ = {
563 | isa = XCConfigurationList;
564 | buildConfigurations = (
565 | 91F1EF3E17F2D4FD00B5017E /* Debug */,
566 | 91F1EF3F17F2D4FD00B5017E /* Release */,
567 | );
568 | defaultConfigurationIsVisible = 0;
569 | defaultConfigurationName = Release;
570 | };
571 | /* End XCConfigurationList section */
572 | };
573 | rootObject = 91F1EF0117F2D4FD00B5017E /* Project object */;
574 | }
575 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollView/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // ITPullToRefreshScrollView
4 | //
5 | // Created by Ilija Tovilo on 9/25/13.
6 | // Copyright (c) 2013 Ilija Tovilo. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ITPullToRefreshScrollView.h"
11 |
12 | @interface AppDelegate : NSObject
16 |
17 | @property (assign) IBOutlet NSWindow *window;
18 | @property (assign) IBOutlet NSTableView *tableView;
19 | @property (assign) IBOutlet ITPullToRefreshScrollView *scrollView;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollView/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // ITPullToRefreshScrollView
4 | //
5 | // Created by Ilija Tovilo on 9/25/13.
6 | // Copyright (c) 2013 Ilija Tovilo. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "ITPullToRefreshScrollView.h"
11 |
12 |
13 | @interface AppDelegate () {
14 | NSMutableArray *data;
15 | }
16 | @end
17 |
18 |
19 | @implementation AppDelegate
20 |
21 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
22 | {
23 | data = [@[ ] mutableCopy];
24 | self.scrollView.refreshableEdges = ITPullToRefreshEdgeTop | ITPullToRefreshEdgeBottom;
25 | [self.tableView reloadData];
26 | }
27 |
28 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
29 | return data.count;
30 | }
31 |
32 | - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
33 | NSTableCellView *cellView = [tableView makeViewWithIdentifier:@"default" owner:self];
34 | cellView.textField.stringValue = data[row];
35 |
36 | return cellView;
37 | }
38 |
39 | - (void)pullToRefreshView:(ITPullToRefreshScrollView *)scrollView didStartRefreshingEdge:(ITPullToRefreshEdge)edge {
40 | double delayInSeconds = ((arc4random() % 10) / 10.0) * 5.0;
41 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
42 | dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^(void){
43 | dispatch_sync(dispatch_get_main_queue(), ^{
44 | if (edge & ITPullToRefreshEdgeTop) {
45 | [data insertObject:@"Test 1" atIndex:0];
46 | [data insertObject:@"Test 2" atIndex:0];
47 | } else if (edge & ITPullToRefreshEdgeBottom) {
48 | for (int i = 0; i < 50; i++) {
49 | [data addObject:[NSString stringWithFormat:@"Test %d", i]];
50 | }
51 | }
52 |
53 | [scrollView stopRefreshingEdge:edge];
54 | });
55 | });
56 | }
57 |
58 | - (void)pullToRefreshView:(ITPullToRefreshScrollView *)scrollView didStopRefreshingEdge:(ITPullToRefreshEdge)edge {
59 | NSRange range;
60 |
61 | if (edge & ITPullToRefreshEdgeTop) {
62 | range = NSMakeRange(0, 2);
63 | } else {
64 | range = NSMakeRange(data.count - 50, 50);
65 | }
66 |
67 | [self.tableView beginUpdates];
68 | {
69 | [self.tableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range]
70 | withAnimation:NSTableViewAnimationSlideDown];
71 | }
72 | [self.tableView endUpdates];
73 | }
74 |
75 | @end
76 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollView/ITPullToRefreshScrollView-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | ${MACOSX_DEPLOYMENT_TARGET}
27 | NSHumanReadableCopyright
28 | Copyright © 2013 Ilija Tovilo. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollView/ITPullToRefreshScrollView-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | #ifdef __OBJC__
8 | #import
9 | #endif
10 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollView/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollView/en.lproj/Credits.rtf:
--------------------------------------------------------------------------------
1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;}
2 | {\colortbl;\red255\green255\blue255;}
3 | \paperw9840\paperh8400
4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
5 |
6 | \f0\b\fs24 \cf0 Engineering:
7 | \b0 \
8 | Some people\
9 | \
10 |
11 | \b Human Interface Design:
12 | \b0 \
13 | Some other people\
14 | \
15 |
16 | \b Testing:
17 | \b0 \
18 | Hopefully not nobody\
19 | \
20 |
21 | \b Documentation:
22 | \b0 \
23 | Whoever\
24 | \
25 |
26 | \b With special thanks to:
27 | \b0 \
28 | Mom\
29 | }
30 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollView/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollView/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // ITPullToRefreshScrollView
4 | //
5 | // Created by Ilija Tovilo on 9/25/13.
6 | // Copyright (c) 2013 Ilija Tovilo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | int main(int argc, const char * argv[])
12 | {
13 | return NSApplicationMain(argc, argv);
14 | }
15 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollViewTests/ITPullToRefreshScrollViewTests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollViewTests/ITPullToRefreshScrollViewTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // ITPullToRefreshScrollViewTests.m
3 | // ITPullToRefreshScrollViewTests
4 | //
5 | // Created by Ilija Tovilo on 9/25/13.
6 | // Copyright (c) 2013 Ilija Tovilo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ITPullToRefreshScrollViewTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation ITPullToRefreshScrollViewTests
16 |
17 | - (void)setUp
18 | {
19 | [super setUp];
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 | }
22 |
23 | - (void)tearDown
24 | {
25 | // Put teardown code here. This method is called after the invocation of each test method in the class.
26 | [super tearDown];
27 | }
28 |
29 | - (void)testExample
30 | {
31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
32 | }
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/ITPullToRefreshScrollView/ITPullToRefreshScrollViewTests/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction, and
10 | distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright
13 | owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all other entities
16 | that control, are controlled by, or are under common control with that entity.
17 | For the purposes of this definition, "control" means (i) the power, direct or
18 | indirect, to cause the direction or management of such entity, whether by
19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20 | outstanding shares, or (iii) beneficial ownership of such entity.
21 |
22 | "You" (or "Your") shall mean an individual or Legal Entity exercising
23 | permissions granted by this License.
24 |
25 | "Source" form shall mean the preferred form for making modifications, including
26 | but not limited to software source code, documentation source, and configuration
27 | files.
28 |
29 | "Object" form shall mean any form resulting from mechanical transformation or
30 | translation of a Source form, including but not limited to compiled object code,
31 | generated documentation, and conversions to other media types.
32 |
33 | "Work" shall mean the work of authorship, whether in Source or Object form, made
34 | available under the License, as indicated by a copyright notice that is included
35 | in or attached to the work (an example is provided in the Appendix below).
36 |
37 | "Derivative Works" shall mean any work, whether in Source or Object form, that
38 | is based on (or derived from) the Work and for which the editorial revisions,
39 | annotations, elaborations, or other modifications represent, as a whole, an
40 | original work of authorship. For the purposes of this License, Derivative Works
41 | shall not include works that remain separable from, or merely link (or bind by
42 | name) to the interfaces of, the Work and Derivative Works thereof.
43 |
44 | "Contribution" shall mean any work of authorship, including the original version
45 | of the Work and any modifications or additions to that Work or Derivative Works
46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work
47 | by the copyright owner or by an individual or Legal Entity authorized to submit
48 | on behalf of the copyright owner. For the purposes of this definition,
49 | "submitted" means any form of electronic, verbal, or written communication sent
50 | to the Licensor or its representatives, including but not limited to
51 | communication on electronic mailing lists, source code control systems, and
52 | issue tracking systems that are managed by, or on behalf of, the Licensor for
53 | the purpose of discussing and improving the Work, but excluding communication
54 | that is conspicuously marked or otherwise designated in writing by the copyright
55 | owner as "Not a Contribution."
56 |
57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
58 | of whom a Contribution has been received by Licensor and subsequently
59 | incorporated within the Work.
60 |
61 | 2. Grant of Copyright License.
62 |
63 | Subject to the terms and conditions of this License, each Contributor hereby
64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
65 | irrevocable copyright license to reproduce, prepare Derivative Works of,
66 | publicly display, publicly perform, sublicense, and distribute the Work and such
67 | Derivative Works in Source or Object form.
68 |
69 | 3. Grant of Patent License.
70 |
71 | Subject to the terms and conditions of this License, each Contributor hereby
72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
73 | irrevocable (except as stated in this section) patent license to make, have
74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where
75 | such license applies only to those patent claims licensable by such Contributor
76 | that are necessarily infringed by their Contribution(s) alone or by combination
77 | of their Contribution(s) with the Work to which such Contribution(s) was
78 | submitted. If You institute patent litigation against any entity (including a
79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
80 | Contribution incorporated within the Work constitutes direct or contributory
81 | patent infringement, then any patent licenses granted to You under this License
82 | for that Work shall terminate as of the date such litigation is filed.
83 |
84 | 4. Redistribution.
85 |
86 | You may reproduce and distribute copies of the Work or Derivative Works thereof
87 | in any medium, with or without modifications, and in Source or Object form,
88 | provided that You meet the following conditions:
89 |
90 | You must give any other recipients of the Work or Derivative Works a copy of
91 | this License; and
92 | You must cause any modified files to carry prominent notices stating that You
93 | changed the files; and
94 | You must retain, in the Source form of any Derivative Works that You distribute,
95 | all copyright, patent, trademark, and attribution notices from the Source form
96 | of the Work, excluding those notices that do not pertain to any part of the
97 | Derivative Works; and
98 | If the Work includes a "NOTICE" text file as part of its distribution, then any
99 | Derivative Works that You distribute must include a readable copy of the
100 | attribution notices contained within such NOTICE file, excluding those notices
101 | that do not pertain to any part of the Derivative Works, in at least one of the
102 | following places: within a NOTICE text file distributed as part of the
103 | Derivative Works; within the Source form or documentation, if provided along
104 | with the Derivative Works; or, within a display generated by the Derivative
105 | Works, if and wherever such third-party notices normally appear. The contents of
106 | the NOTICE file are for informational purposes only and do not modify the
107 | License. You may add Your own attribution notices within Derivative Works that
108 | You distribute, alongside or as an addendum to the NOTICE text from the Work,
109 | provided that such additional attribution notices cannot be construed as
110 | modifying the License.
111 | You may add Your own copyright statement to Your modifications and may provide
112 | additional or different license terms and conditions for use, reproduction, or
113 | distribution of Your modifications, or for any such Derivative Works as a whole,
114 | provided Your use, reproduction, and distribution of the Work otherwise complies
115 | with the conditions stated in this License.
116 |
117 | 5. Submission of Contributions.
118 |
119 | Unless You explicitly state otherwise, any Contribution intentionally submitted
120 | for inclusion in the Work by You to the Licensor shall be under the terms and
121 | conditions of this License, without any additional terms or conditions.
122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of
123 | any separate license agreement you may have executed with Licensor regarding
124 | such Contributions.
125 |
126 | 6. Trademarks.
127 |
128 | This License does not grant permission to use the trade names, trademarks,
129 | service marks, or product names of the Licensor, except as required for
130 | reasonable and customary use in describing the origin of the Work and
131 | reproducing the content of the NOTICE file.
132 |
133 | 7. Disclaimer of Warranty.
134 |
135 | Unless required by applicable law or agreed to in writing, Licensor provides the
136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
138 | including, without limitation, any warranties or conditions of TITLE,
139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
140 | solely responsible for determining the appropriateness of using or
141 | redistributing the Work and assume any risks associated with Your exercise of
142 | permissions under this License.
143 |
144 | 8. Limitation of Liability.
145 |
146 | In no event and under no legal theory, whether in tort (including negligence),
147 | contract, or otherwise, unless required by applicable law (such as deliberate
148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be
149 | liable to You for damages, including any direct, indirect, special, incidental,
150 | or consequential damages of any character arising as a result of this License or
151 | out of the use or inability to use the Work (including but not limited to
152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or
153 | any and all other commercial damages or losses), even if such Contributor has
154 | been advised of the possibility of such damages.
155 |
156 | 9. Accepting Warranty or Additional Liability.
157 |
158 | While redistributing the Work or Derivative Works thereof, You may choose to
159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or
160 | other liability obligations and/or rights consistent with this License. However,
161 | in accepting such obligations, You may act only on Your own behalf and on Your
162 | sole responsibility, not on behalf of any other Contributor, and only if You
163 | agree to indemnify, defend, and hold each Contributor harmless for any liability
164 | incurred by, or claims asserted against, such Contributor by reason of your
165 | accepting any such warranty or additional liability.
166 |
167 | END OF TERMS AND CONDITIONS
168 |
169 | APPENDIX: How to apply the Apache License to your work
170 |
171 | To apply the Apache License to your work, attach the following boilerplate
172 | notice, with the fields enclosed by brackets "[]" replaced with your own
173 | identifying information. (Don't include the brackets!) The text should be
174 | enclosed in the appropriate comment syntax for the file format. We also
175 | recommend that a file or class name and description of purpose be included on
176 | the same "printed page" as the copyright notice for easier identification within
177 | third-party archives.
178 |
179 | Copyright [yyyy] [name of copyright owner]
180 |
181 | Licensed under the Apache License, Version 2.0 (the "License");
182 | you may not use this file except in compliance with the License.
183 | You may obtain a copy of the License at
184 |
185 | http://www.apache.org/licenses/LICENSE-2.0
186 |
187 | Unless required by applicable law or agreed to in writing, software
188 | distributed under the License is distributed on an "AS IS" BASIS,
189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 | See the License for the specific language governing permissions and
191 | limitations under the License.
192 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ITPullToRefreshScrollView
2 | =========================
3 |
4 | `ITPullToRefreshScrollView` is a subclass of `NSScrollView` which allows iOS 7 style refreshing by pulling.
5 | `ITPullToRefreshScrollView` was created for [Play by Play](http://playbyplayapp.com), development funded by [David Keegan](http://davidkeegan.com).
6 |
7 | 
8 |
9 |
10 | Thanks to
11 | ---------
12 |
13 | - [Sasmito Adibowo](https://github.com/adib), a lot of code is based on [RefreshableScrollView](https://github.com/adib/RefreshableScrollView)
14 | - [Abhi Beckert](https://github.com/abhibeckert) for the [DuxScrollViewAnimation class](https://github.com/abhibeckert/Dux/blob/master/Dux/DuxScrollViewAnimation.m).
15 | - [NachoSoto](https://github.com/NachoSoto) for the [NSBKeyframeAnimation class](https://github.com/NachoSoto/NSBKeyframeAnimation)
16 |
17 |
18 | Usage
19 | -----
20 |
21 | ### Include files in project
22 |
23 | `ITPullToRefreshScrollView` has two submodules.
24 | You need to copy the following files:
25 |
26 | *Files under /ITPullToRefreshScrollView/Classes/*
27 |
28 | * `ITPullToRefreshScrollView.h`
29 | * `ITPullToRefreshScrollView.m`
30 | * `ITPullToRefreshClipView.h`
31 | * `ITPullToRefreshClipView.m`
32 | * `ITPullToRefreshEdgeView.h`
33 | * `ITPullToRefreshEdgeView.m`
34 | * `DuxScrollViewAnimation.h`
35 | * `DuxScrollViewAnimation.m`
36 |
37 | -----------
38 |
39 | *Files under /Modules/ITProgressIndicator/ITProgressIndicator/Classes/*
40 |
41 | * `ITProgressIndicator.h`
42 | * `ITProgressIndicator.m`
43 | * `NSBezierPath+Geometry.h`
44 | * `NSBezierPath+Geometry.m`
45 |
46 | -----------
47 |
48 | *Files under /Modules/NSBKeyframeAnimation/NSBKeyframeAnimation/Classes/NSBKeyframeAnimation/*
49 |
50 | * `NSBKeyframeAnimation.h`
51 | * `NSBKeyframeAnimation.m`
52 | * `NSBKeyframeAnimationFunctions.h`
53 | * `NSBKeyframeAnimationFunctions.c`
54 |
55 | -----------
56 |
57 | Make sure to copy them to the project, and to add them to the target.
58 |
59 |
60 | ### Use in a project
61 |
62 | **Make sure to check out the sample project.**
63 |
64 | Set the custom class of your scroll view in Interface Builder to `ITPullToRefreshScrollView`.
65 | Next you need to connect a `IBOutlet` to your scroll view.
66 | ``` objc
67 | @property (assign) IBOutlet ITPullToRefreshScrollView *scrollView;
68 | ```
69 |
70 | Then you can configure in code, which edges of the scroll view should be refreshable.
71 | ``` objc
72 | // Make top & bottom refreshable
73 | self.scrollView.refreshableEdges = ITPullToRefreshEdgeTop | ITPullToRefreshEdgeBottom;
74 | ```
75 |
76 | To receive notifications from the scroll view, you need to create a delegate.
77 | To do this, your delegate class must implement the `ITPullToRefreshScrollViewDelegate` protocol.
78 | You can then implement the following methods.
79 | ``` objc
80 | /**
81 | * This method get's invoked when the scroll view started refreshing
82 | *
83 | * @param scrollView - The scroll view that started refreshing
84 | * @param edge - The edge that started refreshing
85 | */
86 | - (void)pullToRefreshView:(ITPullToRefreshScrollView *)scrollView
87 | didStartRefreshingEdge:(ITPullToRefreshEdge)edge {
88 |
89 | // Do stuff that takes very long
90 |
91 | // Don't forget to call this line!
92 | [scrollView stopRefreshingEdge:edge];
93 | }
94 |
95 | /**
96 | * This method get's invoked when the scroll view stopped refreshing
97 | *
98 | * @param scrollView - The scroll view that stopped refreshing
99 | * @param edge - The edge that stopped refreshing
100 | */
101 | - (void)pullToRefreshView:(ITPullToRefreshScrollView *)scrollView
102 | didStopRefreshingEdge:(ITPullToRefreshEdge)edge {
103 |
104 | // Do UI stuff
105 | }
106 | ```
107 |
108 | To receive notifications, you finally have to set the delegate of the scroll view.
109 | ``` objc
110 | self.scrollView.delegate = self;
111 | ```
112 |
113 | ### Customisation
114 |
115 | To create custom `ITPullToRefreshEdgeView` subclasses, override the following methods:
116 |
117 | ``` objc
118 | // --------------------------
119 | // ------------------- Events
120 | // --------------------------
121 |
122 | /**
123 | * This method is called when the edge is triggered
124 | *
125 | * @param scrollView - The sender scroll view
126 | */
127 | - (void)pullToRefreshScrollViewDidTriggerRefresh:(ITPullToRefreshScrollView *)scrollView;
128 |
129 | /**
130 | * This method is called when the edge is untriggered
131 | *
132 | * @param scrollView - The sender scroll view
133 | */
134 | - (void)pullToRefreshScrollViewDidUntriggerRefresh:(ITPullToRefreshScrollView *)scrollView;
135 |
136 | /**
137 | * This method is called when the edge starts refreshing
138 | *
139 | * @param scrollView - The sender scroll view
140 | */
141 | - (void)pullToRefreshScrollViewDidStartRefreshing:(ITPullToRefreshScrollView *)scrollView;
142 |
143 | /**
144 | * This method is called when the edge stops refreshing
145 | *
146 | * @param scrollView - The sender scroll view
147 | */
148 | - (void)pullToRefreshScrollViewDidStopRefreshing:(ITPullToRefreshScrollView *)scrollView;
149 |
150 | /**
151 | * This method is called when part of the edge view becomes visible
152 | *
153 | * @param scrollView - The sender scroll view
154 | * @param progress - The amount of the edge view that is visible (from 0.0 to 1.0)
155 | */
156 | - (void)pullToRefreshScrollView:(ITPullToRefreshScrollView *)scrollView didScrollWithProgress:(CGFloat)progress;
157 |
158 |
159 |
160 | // --------------------------
161 | // ------------ Customisation
162 | // --------------------------
163 |
164 | /**
165 | * Override this to remove the progress indicator and create other components
166 | */
167 | - (void)installComponents;
168 |
169 | /**
170 | * The height of the edge view.
171 | * Override this method to achieve a custom edge view height.
172 | *
173 | * @return The height of the edge view
174 | */
175 | - (CGFloat)edgeViewHeight;
176 |
177 | /**
178 | * Override this method to draw a custom background.
179 | * You can also just override `drawRect:` and to all the drawing on your own.
180 | *
181 | * @param The rect which should be drawn on
182 | */
183 | - (void)drawBackgroundInRect:(NSRect)dirtyRect;
184 | ```
185 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iluuu1994/ITPullToRefreshScrollView/70ffb846b5ca868634a5ff0dfcfc5a7452785022/demo.gif
--------------------------------------------------------------------------------