Browse files

Further refactoring and improving the coverflow implementation.

  • Loading branch information...
1 parent db5816b commit 1dc3079438076ebd42b5dc9fb47207ccb08c0662 Devin Ross committed Jul 24, 2012
Showing with 171 additions and 101 deletions.
  1. +20 −7 src/TapkuLibrary/TKCoverflowView.h
  2. +151 −94 src/TapkuLibrary/TKCoverflowView.m
View
27 src/TapkuLibrary/TKCoverflowView.h
@@ -44,22 +44,27 @@
NSMutableArray *views; // only covers view (no nulls)
NSMutableArray *yard; // covers ready for reuse (ie. graveyard)
- float origin;
BOOL movingRight;
UIView *currentTouch;
NSRange deck;
- int margin, coverBuffer, currentIndex, numberOfCovers;
- CGSize coverSize,currentSize;
- float coverSpacing,coverAngle,spaceFromCurrent;
+ NSInteger margin, coverBuffer, currentIndex, numberOfCovers;
+ CGSize coverSize;
+ CGFloat coverSpacing,coverAngle,spaceFromCurrent;
CATransform3D leftTransform, rightTransform;
+ CGSize boundSize;
+
// SPEED
- int pos;
- long velocity;
+ NSInteger pos;
+ CGFloat velocity;
+
+ BOOL isDragging;
+ BOOL gliding;
+ NSInteger movingIndex;
}
@@ -95,7 +100,15 @@
*/
- (TKCoverflowCoverView*) coverAtIndex:(int)index; // returns nil if cover is outside active range
-- (int) indexOfFrontCoverView; // deprecated
+/** Returns the visible covers on screen.
+ @return An array of visible covers.
+ */
+- (NSArray*) visibleCovers;
+
+/** Returns the range covers on screen.
+ @return A range of visible covers.
+ */
+- (NSRange) visibleRange;
/** Sets the foremost cover.
@param index The index of the cover that will become the foremost cover.
View
245 src/TapkuLibrary/TKCoverflowView.m
@@ -44,30 +44,36 @@
#pragma mark -
@interface TKCoverflowView (hidden)
-- (void) animateToIndex:(int)index animated:(BOOL)animated;
-- (void) load;
-- (void) setup;
+- (void) animateToIndex:(int)index animated:(BOOL)animated;
+- (void) _load;
+- (void) _setup;
- (void) newrange;
-- (void) setupTransforms;
+- (void) _setupTransforms;
- (void) adjustViewHeirarchy;
- (void) deplaceAlbumsFrom:(int)start to:(int)end;
- (void) deplaceAlbumsAtIndex:(int)cnt;
- (BOOL) placeAlbumsFrom:(int)start to:(int)end;
-- (void) placeAlbumAtIndex:(int)cnt;
+- (void) placeAlbumAtIndex:(NSInteger)cnt;
- (void) snapToAlbum:(BOOL)animated;
@end
#pragma mark -
+@implementation TKCoverflowView
+@synthesize coverflowDelegate, dataSource, coverSize, numberOfCovers, coverSpacing, coverAngle, spaceInFront;
-@implementation TKCoverflowView (hidden)
-
+#pragma mark Initialize
+- (id) initWithFrame:(CGRect)frame {
+ if(!(self=[super initWithFrame:frame])) return nil;
+ [self _load];
+ return self;
+}
- (void) awakeFromNib{
- [self load];
+ [self _load];
}
#pragma mark Setup
-- (void) setupTransforms{
+- (void) _setupTransforms{
leftTransform = CATransform3DMakeRotation(coverAngle, 0, 1, 0);
leftTransform = CATransform3DConcat(leftTransform,CATransform3DMakeTranslation(-spaceFromCurrent, 0, -self.spaceInFront));
@@ -76,32 +82,32 @@ - (void) setupTransforms{
rightTransform = CATransform3DConcat(rightTransform,CATransform3DMakeTranslation(spaceFromCurrent, 0, -self.spaceInFront));
}
-- (void) load{
+- (void) _load{
self.backgroundColor = [UIColor blackColor];
numberOfCovers = 0;
coverSpacing = COVER_SPACING;
coverAngle = SIDE_COVER_ANGLE;
self.spaceInFront = FRONT_SPACE;
self.showsHorizontalScrollIndicator = NO;
super.delegate = self;
- origin = self.contentOffset.x;
yard = [[NSMutableArray alloc] init];
views = [[NSMutableArray alloc] init];
coverSize = CGSizeMake(224, 224);
spaceFromCurrent = coverSize.width/2.4;
- [self setupTransforms];
+ [self _setupTransforms];
CATransform3D sublayerTransform = CATransform3DIdentity;
sublayerTransform.m34 = -0.001;
[self.layer setSublayerTransform:sublayerTransform];
currentIndex = -1;
- currentSize = self.frame.size;
+
+
}
-- (void) setup{
+- (void) _setup{
currentIndex = -1;
for(UIView *v in views) [v removeFromSuperview];
@@ -119,21 +125,23 @@ - (void) setup{
for (unsigned i = 0; i < numberOfCovers; i++) [coverViews addObject:[NSNull null]];
deck = NSMakeRange(0, 0);
- currentSize = self.frame.size;
+ CGSize size = self.frame.size;
margin = (self.frame.size.width / 2);
- self.contentSize = CGSizeMake( (coverSpacing) * (numberOfCovers-1) + (margin*2) , currentSize.height);
- coverBuffer = (int) ((currentSize.width - coverSize.width) / coverSpacing) + 3;
+ self.contentSize = CGSizeMake( (coverSpacing) * (numberOfCovers-1) + (margin*2) , size.height);
+ coverBuffer = (int) ((size.width - coverSize.width) / coverSpacing) + 3;
movingRight = YES;
- currentSize = self.frame.size;
currentIndex = 0;
+ movingIndex = 0;
self.contentOffset = CGPointZero;
[self newrange];
[self animateToIndex:currentIndex animated:NO];
+
+ boundSize = self.frame.size;
}
@@ -160,31 +168,50 @@ - (void) deplaceAlbumsAtIndex:(int)cnt{
}
- (BOOL) placeAlbumsFrom:(int)start to:(int)end{
if(start >= end) return NO;
+
+
for(int cnt=start;cnt<= end;cnt++)
[self placeAlbumAtIndex:cnt];
+
return YES;
}
-- (void) placeAlbumAtIndex:(int)cnt{
- if(cnt >= [coverViews count]) return;
+- (void) placeAlbumAtIndex:(NSInteger)index{
+ if(index >= [coverViews count]) return;
- if([coverViews objectAtIndex:cnt] == [NSNull null]){
+ if([coverViews objectAtIndex:index] == [NSNull null]){
+
+ TKCoverflowCoverView *cover = [self.dataSource coverflowView:self coverAtIndex:index];
+ cover.transform = CGAffineTransformIdentity;
+ [coverViews replaceObjectAtIndex:index withObject:cover];
- TKCoverflowCoverView *cover = [self.dataSource coverflowView:self coverAtIndex:cnt];
- [coverViews replaceObjectAtIndex:cnt withObject:cover];
+ [cover.layer removeAllAnimations];
+
+
+ CGSize size = self.frame.size;
+
CGRect r = cover.frame;
- r.origin.y = currentSize.height / 2 - (coverSize.height/2) - (coverSize.height/16);
- r.origin.x = (currentSize.width/2 - (coverSize.width/ 2)) + (coverSpacing) * cnt;
+ r.origin.y = size.height/2 - (coverSize.height/2) - (coverSize.height/16);
+ r.origin.x = (size.width/2 - (coverSize.width/ 2)) + (coverSpacing) * index;
cover.frame = r;
+
+
- [self addSubview:cover];
- if(cnt > currentIndex){
- cover.layer.transform = rightTransform;
+ CATransform3D tr = CATransform3DIdentity;
+
+ if(index < movingIndex)
+ tr = leftTransform;
+
+ else if(index > movingIndex){
+ tr = rightTransform;
[self sendSubviewToBack:cover];
+
}
- else
- cover.layer.transform = leftTransform;
+
+ cover.layer.transform = tr;
+
+ [self addSubview:cover];
[views addObject:cover];
}
@@ -202,7 +229,6 @@ - (void) newrange{
if(movingRight){
[self deplaceAlbumsFrom:loc to:MIN(newLocation,loc+len)];
[self placeAlbumsFrom:MAX(loc+len,newLocation) to:newLocation+newLength];
-
}else{
[self deplaceAlbumsFrom:MAX(newLength+newLocation,loc) to:loc+len];
[self placeAlbumsFrom:newLocation to:newLocation+newLength];
@@ -211,7 +237,7 @@ - (void) newrange{
NSRange spectrum = NSMakeRange(0, numberOfCovers);
deck = NSIntersectionRange(spectrum, NSMakeRange(newLocation, newLength));
-
+
}
- (void) adjustViewHeirarchy{
@@ -237,23 +263,24 @@ - (void) snapToAlbum:(BOOL)animated{
UIView *v = [coverViews objectAtIndex:currentIndex];
if((NSObject*)v!=[NSNull null]){
- [self setContentOffset:CGPointMake(v.center.x - (currentSize.width/2), 0) animated:animated];
+ CGSize size = self.frame.size;
+ [self setContentOffset:CGPointMake(v.center.x - (size.width/2), 0) animated:animated];
}else{
[self setContentOffset:CGPointMake(coverSpacing*currentIndex, 0) animated:animated];
}
}
- (void) animateToIndex:(int)index animated:(BOOL)animated{
+ NSString *ID = [NSString stringWithFormat:@"%d",index];
+ //if(velocity>200) animated = NO;
- NSString *string = [NSString stringWithFormat:@"%d",currentIndex];
- if(velocity> 200) animated = NO;
-
+
if(animated){
- float speed = 0.2;
- if(velocity>80)speed=0.05;
- [UIView beginAnimations:string context:nil];
- [UIView setAnimationDuration:speed];
- [UIView setAnimationCurve:UIViewAnimationCurveLinear];
+ //float speed = 0.2;
+ //if(velocity>80)speed=0.05;
+ [UIView beginAnimations:ID context:nil];
+ [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
+ [UIView setAnimationDuration:0.3f];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
@@ -272,47 +299,44 @@ - (void) animateToIndex:(int)index animated:(BOOL)animated{
}
- (void) animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
- if([finished boolValue]) [self adjustViewHeirarchy];
- if([finished boolValue] && [animationID intValue] == currentIndex) [self.coverflowDelegate coverflowView:self coverAtIndexWasBroughtToFront:currentIndex];
+ if(![finished boolValue]) return;
+
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(adjustViewHeirarchy) object:nil];
+ [self performSelector:@selector(adjustViewHeirarchy) withObject:nil afterDelay:0.3f];
+
+ if([animationID intValue] == movingIndex)
+ [self.coverflowDelegate coverflowView:self coverAtIndexWasBroughtToFront:movingIndex];
}
-@end
-#pragma mark -
-@implementation TKCoverflowView
-@synthesize coverflowDelegate, dataSource, coverSize, numberOfCovers, coverSpacing, coverAngle, spaceInFront;
-
-- (id) initWithFrame:(CGRect)frame {
- if(!(self=[super initWithFrame:frame])) return nil;
- [self load];
- currentSize = frame.size;
- return self;
-}
+#pragma mark View Bounds
- (void) _adjustToBounds{
- currentSize = self.frame.size;
+ CGSize size = self.frame.size;
+
margin = (self.frame.size.width / 2);
self.contentSize = CGSizeMake( (coverSpacing) * (numberOfCovers-1) + (margin*2) , self.frame.size.height);
- coverBuffer = (int)((currentSize.width - coverSize.width) / coverSpacing) + 3;
+ coverBuffer = (int)((size.width - coverSize.width) / coverSpacing) + 3;
for(UIView *v in views){
v.layer.transform = CATransform3DIdentity;
CGRect r = v.frame;
- r.origin.y = currentSize.height / 2 - (coverSize.height/2) - (coverSize.height/16);
+ r.origin.y = size.height / 2 - (coverSize.height/2) - (coverSize.height/16);
v.frame = r;
}
for(int i= deck.location; i < deck.location + deck.length; i++){
if([coverViews objectAtIndex:i] != [NSNull null]){
UIView *cover = [coverViews objectAtIndex:i];
CGRect r = cover.frame;
- r.origin.x = (currentSize.width/2 - (coverSize.width/ 2)) + (coverSpacing) * i;
+ r.origin.x = (size.width/2 - (coverSize.width/ 2)) + (coverSpacing) * i;
cover.frame = r;
}
}
@@ -326,48 +350,57 @@ - (void) setFrame:(CGRect)frame{
}
- (void) setBounds:(CGRect)bounds{
[super setBounds:bounds];
+ if(boundSize.width == bounds.size.width && boundSize.height == bounds.size.height) return;
[self _adjustToBounds];
}
- (NSInteger) calculatedIndexWithContentOffset:(CGPoint)point{
- CGFloat num = numberOfCovers;
- CGFloat per = point.x / (self.contentSize.width - currentSize.width);
- CGFloat ind = num * per;
+ CGSize size = self.frame.size;
+ CGFloat per = point.x / (self.contentSize.width - size.width);
+ CGFloat ind = numberOfCovers * per;
CGFloat mi = ind / (numberOfCovers/2);
mi = 1 - mi;
mi = mi / 2;
int index = (int)(ind+mi);
index = MIN(MAX(0,index),numberOfCovers-1);
-
return index;
}
- (void) layoutSubviews{
- velocity = abs(pos - self.contentOffset.x);
+ velocity = abs(pos-self.contentOffset.x);
+ movingRight = self.contentOffset.x - pos > 0 ? YES : NO;
pos = self.contentOffset.x;
- movingRight = self.contentOffset.x - origin > 0 ? YES : NO;
- origin = self.contentOffset.x;
+
- CGFloat num = numberOfCovers;
- CGFloat per = self.contentOffset.x / (self.contentSize.width - currentSize.width);
- CGFloat ind = num * per;
- CGFloat mi = ind / (numberOfCovers/2);
- mi = 1 - mi;
- mi = mi / 2;
- int index = (int)(ind+mi);
- index = MIN(MAX(0,index),numberOfCovers-1);
- if(index == currentIndex) return;
+
+
+ NSInteger index = [self calculatedIndexWithContentOffset:self.contentOffset];
+ NSInteger oldIndex = movingIndex;
+ currentIndex=index;
+
- currentIndex = index;
+ [CATransaction begin];
+ [CATransaction setAnimationDuration:0];
[self newrange];
+ [CATransaction commit];
+
+
+
+ if(!gliding || isDragging){
+ movingIndex = index;
+
+ if(oldIndex!=movingIndex)
+ [self animateToIndex:movingIndex animated:YES];
+
+ }else{
+ [self animateToIndex:movingIndex animated:YES];
+ }
- if(velocity < 180 || currentIndex < 15 || currentIndex > (numberOfCovers - 16))
- [self animateToIndex:index animated:YES];
}
@@ -380,12 +413,8 @@ - (TKCoverflowCoverView *) coverAtIndex:(int)index{
return [coverViews objectAtIndex:index];
return nil;
}
-- (NSInteger) indexOfFrontCoverView{
- return currentIndex;
-}
- (void) bringCoverAtIndexToFront:(int)index animated:(BOOL)animated{
-
if(index == currentIndex) return;
currentIndex = index;
@@ -399,14 +428,16 @@ - (TKCoverflowCoverView*) dequeueReusableCoverView{
TKCoverflowCoverView *v = [yard lastObject];
v.layer.transform = CATransform3DIdentity;
+ [v.layer removeAllAnimations];
[yard removeLastObject];
-
+
return v;
}
#pragma mark Touch Events
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
+
if(touch.view != self && [touch locationInView:touch.view].y < coverSize.height){
currentTouch = touch.view;
@@ -436,44 +467,70 @@ - (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
}
#pragma mark UIScrollView Delegate
-- (void) scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
- *targetContentOffset = CGPointMake(coverSpacing * [self calculatedIndexWithContentOffset:*targetContentOffset], 0);
+- (void) scrollViewWillBeginDragging:(UIScrollView *)scrollView{
+ isDragging = YES;
+ gliding = NO;
+ [self setNeedsLayout];
+}
+- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
+ isDragging = NO;
+}
+- (void) scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocityTangent targetContentOffset:(inout CGPoint *)targetContentOffset{
+
+
+ NSInteger i = [self calculatedIndexWithContentOffset:*targetContentOffset];
+ *targetContentOffset = CGPointMake(coverSpacing * i, 0);
+
+ if(fabs(velocityTangent.x)<2.0f) return;
+
+ isDragging = NO;
+ gliding = YES;
+ movingIndex = i;
+
}
#pragma mark Properties
- (void) setNumberOfCovers:(int)cov{
numberOfCovers = cov;
- [self setup];
+ [self _setup];
}
- (void) setCoverSpacing:(float)space{
coverSpacing = space;
- [self setupTransforms];
- [self setup];
+ [self _setupTransforms];
+ [self _setup];
[self layoutSubviews];
}
- (void) setSpaceInFront:(float)front{
spaceInFront = front;
- [self setupTransforms];
- [self setup];
+ [self _setupTransforms];
+ [self _setup];
[self layoutSubviews];
}
- (void) setCoverAngle:(float)f{
coverAngle = f;
- [self setupTransforms];
- [self setup];
+ [self _setupTransforms];
+ [self _setup];
}
- (void) setCoverSize:(CGSize)s{
coverSize = s;
spaceFromCurrent = coverSize.width/2.4;
- [self setupTransforms];
- [self setup];
+ [self _setupTransforms];
+ [self _setup];
}
- (void) setCurrentIndex:(NSInteger)index{
[self bringCoverAtIndexToFront:index animated:NO];
}
- (NSInteger) currentIndex{
- return currentIndex;
+ return movingIndex;
+}
+
+
+- (NSArray*) visibleCovers{
+ return [coverViews subarrayWithRange:deck];
+}
+- (NSRange) visibleRange{
+ return deck;
}
@end

0 comments on commit 1dc3079

Please sign in to comment.