forked from davdroman/Popsicle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DRDynamicSlideShow.m
281 lines (214 loc) · 11.6 KB
/
DRDynamicSlideShow.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
//
// DRDynamicSlideShow.m
// DRDynamicSlideShow
//
// Created by David Román Aguirre on 17/09/13.
// Copyright (c) 2013 David Román Aguirre. All rights reserved.
//
#import "DRDynamicSlideShow.h"
typedef enum {
DRDynamicSlideShowAnimationValueTypeCGFloat = 0,
DRDynamicSlideShowAnimationValueTypeCGPoint = 1,
DRDynamicSlideShowAnimationValueTypeCGSize = 2,
DRDynamicSlideShowAnimationValueTypeCGRect = 3,
DRDynamicSlideShowAnimationValueTypeCGAffineTransform = 4,
DRDynamicSlideShowAnimationValueTypeUIColor = 5,
} DRDynamicSlideShowAnimationValueType;
#pragma mark Interfaces Extensions
@interface DRDynamicSlideShow ()
@property (readwrite, nonatomic) NSInteger numberOfPages;
@end
@interface DRDynamicSlideShowAnimation ()
@property (nonatomic) DRDynamicSlideShowAnimationValueType valueType;
@end
#pragma mark Implementations
@implementation DRDynamicSlideShowAnimation
+ (id)animationForSubview:(UIView *)subview page:(NSInteger)page keyPath:(NSString *)keyPath toValue:(id)toValue delay:(CGFloat)delay {
DRDynamicSlideShowAnimation * animation = [DRDynamicSlideShowAnimation animationForSubview:subview page:page keyPath:keyPath fromValue:[subview valueForKeyPath:keyPath] toValue:toValue delay:delay];
return animation;
}
+ (id)animationForSubview:(UIView *)subview page:(NSInteger)page keyPath:(NSString *)keyPath fromValue:(id)fromValue toValue:(id)toValue delay:(CGFloat)delay {
DRDynamicSlideShowAnimation * animation = [[DRDynamicSlideShowAnimation alloc] init];
[animation setSubview:subview];
[animation setPage:page];
[animation setKeyPath:keyPath];
[animation setFromValue:fromValue];
[animation setToValue:toValue];
[animation setDelay:delay];
[animation setValueType:[animation valueDataType]];
return animation;
}
- (DRDynamicSlideShowAnimationValueType)valueDataType {
DRDynamicSlideShowAnimationValueType valueDataType = 0;
if ([self.fromValue isKindOfClass:NSClassFromString(@"NSNumber")]) {
if ([self valueTypeIsEqualTo:@encode(CGFloat)]) {
valueDataType = DRDynamicSlideShowAnimationValueTypeCGFloat;
}
} else if ([self.fromValue isKindOfClass:NSClassFromString(@"NSValue")]) {
if ([self valueTypeIsEqualTo:@encode(CGPoint)]) {
valueDataType = DRDynamicSlideShowAnimationValueTypeCGPoint;
} else if ([self valueTypeIsEqualTo:@encode(CGSize)]) {
valueDataType = DRDynamicSlideShowAnimationValueTypeCGSize;
} else if ([self valueTypeIsEqualTo:@encode(CGRect)]) {
valueDataType = DRDynamicSlideShowAnimationValueTypeCGRect;
} else if ([self valueTypeIsEqualTo:@encode(CGAffineTransform)]) {
valueDataType = DRDynamicSlideShowAnimationValueTypeCGAffineTransform;
}
} else {
valueDataType = DRDynamicSlideShowAnimationValueTypeUIColor;
}
return valueDataType;
}
- (BOOL)valueTypeIsEqualTo:(const char *)typeChar {
NSString * valueDataTypeString = [[NSString alloc] initWithCString:[self.fromValue objCType] encoding:NSUTF8StringEncoding];
NSString * comparisonDataTypeString = [[NSString alloc] initWithCString:typeChar encoding:NSUTF8StringEncoding];
if ([valueDataTypeString isEqualToString:comparisonDataTypeString]) {
return YES;
}
return NO;
}
- (void)performWithPercentage:(CGFloat)percentage {
percentage = MAX((percentage-self.delay)/(1-self.delay), 0);
switch (self.valueType) {
case DRDynamicSlideShowAnimationValueTypeCGFloat: {
CGFloat newFloat = [self partialValueWithFromValue:[self.fromValue floatValue] toValue:[self.toValue floatValue] percentage:percentage];
[self.subview setValue:@(newFloat) forKeyPath:self.keyPath];
}
break;
case DRDynamicSlideShowAnimationValueTypeCGPoint: {
CGFloat newX = [self partialValueWithFromValue:[self.fromValue CGPointValue].x toValue:[self.toValue CGPointValue].x percentage:percentage];
CGFloat newY = [self partialValueWithFromValue:[self.fromValue CGPointValue].y toValue:[self.toValue CGPointValue].y percentage:percentage];
[self.subview setValue:[NSValue valueWithCGPoint:CGPointMake(newX, newY)] forKeyPath:self.keyPath];
}
break;
case DRDynamicSlideShowAnimationValueTypeCGSize: {
CGFloat newWidth = [self partialValueWithFromValue:[self.fromValue CGSizeValue].width toValue:[self.toValue CGSizeValue].width percentage:percentage];
CGFloat newHeight = [self partialValueWithFromValue:[self.fromValue CGSizeValue].height toValue:[self.toValue CGSizeValue].height percentage:percentage];
[self.subview setValue:[NSValue valueWithCGSize:CGSizeMake(newWidth, newHeight)] forKeyPath:self.keyPath];
}
break;
case DRDynamicSlideShowAnimationValueTypeCGRect: {
CGFloat newX = [self partialValueWithFromValue:[self.fromValue CGRectValue].origin.x toValue:[self.toValue CGRectValue].origin.x percentage:percentage];
CGFloat newY = [self partialValueWithFromValue:[self.fromValue CGRectValue].origin.y toValue:[self.toValue CGRectValue].origin.y percentage:percentage];
CGFloat newWidth = [self partialValueWithFromValue:[self.fromValue CGRectValue].size.width toValue:[self.toValue CGRectValue].size.width percentage:percentage];
CGFloat newHeight = [self partialValueWithFromValue:[self.fromValue CGRectValue].size.height toValue:[self.toValue CGRectValue].size.height percentage:percentage];
[self.subview setValue:[NSValue valueWithCGRect:CGRectMake(newX, newY, newWidth, newHeight)] forKeyPath:self.keyPath];
}
break;
case DRDynamicSlideShowAnimationValueTypeCGAffineTransform: {
CGFloat newA = [self partialValueWithFromValue:[self.fromValue CGAffineTransformValue].a toValue:[self.toValue CGAffineTransformValue].a percentage:percentage];
CGFloat newB = [self partialValueWithFromValue:[self.fromValue CGAffineTransformValue].b toValue:[self.toValue CGAffineTransformValue].b percentage:percentage];
CGFloat newC = [self partialValueWithFromValue:[self.fromValue CGAffineTransformValue].c toValue:[self.toValue CGAffineTransformValue].c percentage:percentage];
CGFloat newD = [self partialValueWithFromValue:[self.fromValue CGAffineTransformValue].d toValue:[self.toValue CGAffineTransformValue].d percentage:percentage];
CGFloat newTx = [self partialValueWithFromValue:[self.fromValue CGAffineTransformValue].tx toValue:[self.toValue CGAffineTransformValue].tx percentage:percentage];
CGFloat newTy = [self partialValueWithFromValue:[self.fromValue CGAffineTransformValue].ty toValue:[self.toValue CGAffineTransformValue].ty percentage:percentage];
[self.subview setValue:[NSValue valueWithCGAffineTransform:CGAffineTransformMake(newA, newB, newC, newD, newTx, newTy)] forKeyPath:self.keyPath];
}
break;
case DRDynamicSlideShowAnimationValueTypeUIColor: {
CGFloat fromR = 0;
CGFloat fromG = 0;
CGFloat fromB = 0;
CGFloat fromA = 0;
CGFloat toR = 0;
CGFloat toG = 0;
CGFloat toB = 0;
CGFloat toA = 0;
[(UIColor *)self.fromValue getRed:&fromR green:&fromG blue:&fromB alpha:&fromA];
[(UIColor *)self.toValue getRed:&toR green:&toG blue:&toB alpha:&toA];
CGFloat newR = [self partialValueWithFromValue:fromR toValue:toR percentage:percentage];
CGFloat newG = [self partialValueWithFromValue:fromG toValue:toG percentage:percentage];
CGFloat newB = [self partialValueWithFromValue:fromB toValue:toB percentage:percentage];
CGFloat newA = [self partialValueWithFromValue:fromA toValue:toA percentage:percentage];
UIColor * newColor = [[UIColor alloc] initWithRed:newR green:newG blue:newB alpha:newA];
[self.subview setValue:newColor forKeyPath:self.keyPath];
}
break;
default:
break;
}
}
- (CGFloat)partialValueWithFromValue:(CGFloat)fromValue toValue:(CGFloat)toValue percentage:(CGFloat)percentage {
return fromValue+(toValue-fromValue)*percentage;
}
@end
@implementation DRDynamicSlideShow
#pragma mark Basic Defined Methods
- (id)init {
if (self = [super init]) {
animations = [NSMutableArray new];
tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollToNextPage)];
[self addGestureRecognizer:tapGestureRecognizer];
[self setDelegate:self];
[self setPagingEnabled:YES];
[self setShowsHorizontalScrollIndicator:NO];
[self setShowsVerticalScrollIndicator:NO];
[self setScrollsPageOnTap:YES];
}
return self;
}
- (void)scrollToNextPage {
[self resetCurrentAnimations];
[self performCurrentAnimationsWithPercentage:0];
if (currentPage+1 < self.numberOfPages) {
[UIView animateWithDuration:0.425 animations:^{
[self setContentOffset:CGPointMake(self.contentOffset.x+self.frame.size.width, self.contentOffset.y)];
}];
}
}
- (void)addAnimation:(DRDynamicSlideShowAnimation *)animation {
[animations addObject:animation];
}
- (void)addSubview:(UIView *)view {
[super addSubview:view];
if (view.frame.origin.x >= self.contentSize.width) {
NSInteger numberOfPages = floorf(view.frame.origin.x/self.frame.size.width)+1;
[self setNumberOfPages:numberOfPages];
[self setContentSize:CGSizeMake(self.frame.size.width*self.numberOfPages, self.contentSize.height)];
}
}
- (void)addSubview:(UIView *)view onPage:(NSInteger)page {
[view setFrame:CGRectMake(view.frame.origin.x+self.frame.size.width*page, view.frame.origin.y, view.frame.size.width, view.frame.size.height)];
[self addSubview:view];
}
#pragma mark Setter Overrides
- (void)setScrollsPageOnTap:(BOOL)scrollsPageOnTap {
_scrollsPageOnTap = scrollsPageOnTap;
[tapGestureRecognizer setEnabled:scrollsPageOnTap];
}
#pragma mark Useful Methods
- (NSInteger)currentPage {
NSInteger page = floor(self.contentOffset.x / self.frame.size.width);
return page;
}
- (void)resetCurrentAnimations {
NSInteger page = [self currentPage];
currentAnimations = [self animationsForPage:page];
}
- (NSArray *)animationsForPage:(NSInteger)page {
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"page == %i", page];
NSArray * filteredAnimations = [animations filteredArrayUsingPredicate:predicate];
return filteredAnimations;
}
#pragma mark Scroll View Delegate Methods
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self resetCurrentAnimations];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSInteger page = [self currentPage];
if (currentPage != page) {
[self performCurrentAnimationsWithPercentage:(currentPage < page ? 1 : 0)];
currentPage = page;
[self resetCurrentAnimations];
if (self.didReachPageBlock) self.didReachPageBlock(page);
}
CGFloat horizontalScroll = self.contentOffset.x-self.frame.size.width*currentPage;
CGFloat percentage = horizontalScroll/self.frame.size.width;
[self performCurrentAnimationsWithPercentage:percentage];
}
- (void)performCurrentAnimationsWithPercentage:(CGFloat)percentage {
for (DRDynamicSlideShowAnimation * animation in currentAnimations) {
[animation performWithPercentage:percentage];
}
}
@end