Skip to content
Browse files

Added scroll and drag tracking delegate methods to iCarousel

  • Loading branch information...
1 parent 9874166 commit 3d7093530183ad3d017f59c2c073e484207b1a83 @nicklockwood committed Jul 4, 2011
View
2 LICENCE.md
@@ -1,5 +1,5 @@
iCarousel
-version 1.3.2, June 29th, 2011
+version 1.3.3, July 4th, 2011
Copyright (C) 2011 Charcoal Design
View
36 README.md
@@ -19,12 +19,12 @@ Carousel Types
iCarousel supports the following built-in display types:
-iCarouselTypeLinear
-iCarouselTypeRotary
-iCarouselTypeInvertedRotary
-iCarouselTypeCylinder
-iCarouselTypeInvertedCylinder
-iCarouselTypeCoverFlow
+- iCarouselTypeLinear
+- iCarouselTypeRotary
+- iCarouselTypeInvertedRotary
+- iCarouselTypeCylinder
+- iCarouselTypeInvertedCylinder
+- iCarouselTypeCoverFlow
You can also implement your own bespoke style using iCarouselTypeCustom and the `carousel:transformForItemView:withOffset:` delegate method.
@@ -152,6 +152,14 @@ Return a view to be displayed as the placeholder view. As with the regular item
The iCarouselDelegate protocol has the following optional methods:
+ - (void)carouselWillBeginScrollingAnimation:(iCarousel *)carousel;
+
+This method is called whenever the carousel will begin an animated scroll. This can be triggered programatically or automatically after the user finishes scrolling the carousel, as the carousel re-aligns itself.
+
+ - (void)carouselDidEndScrollingAnimation:(iCarousel *)carousel;
+
+This method is called when the carousel ends an animated scroll.
+
- (void)carouselDidScroll:(iCarousel *)carousel;
This method is called whenever the carousel is scrolled. It is called regardless of whether the carousel was scrolled programatically or through user interaction.
@@ -160,6 +168,22 @@ This method is called whenever the carousel is scrolled. It is called regardless
This method is called whenever the carousel scrolls far enough for the currentItemIndex property to change. It is called regardless of whether the item index was updated programatically or through user interaction.
+ - (void)carouselWillBeginDragging:(iCarousel *)carousel;
+
+This method is called when the user begins dragging the carousel. It will not fire if the user taps/clicks the carousel, or if the carousel is scrolled programmatically.
+
+ - (void)carouselDidEndDragging:(iCarousel *)carousel willDecelerate:(BOOL)decelerate;
+
+This method is called when the user stops dragging the carousel. The willDecelerate parameter indicates whether the carousel is travelling fast enough that it needs to decelerate before it stops (i.e. the current index is not necessarily the one it will stop at) or if it will stop where it is. Note that even if willDecelerate is NO, the carousel will still scroll automatically until it aligns exactly on the current index. If you need to know when it has stopped moving completely, use the carouselDidEndScrollingAnimation delegate method. On Mac OS, willDecelerate is always NO when using the scrollwheel because Mac OS implements its own inertia mechanism for scrolling.
+
+ - (void)carouselWillBeginDecelerating:(iCarousel *)carousel;
+
+This method is called when the carousel starts decelerating. it will typically be called immediately after the carouselDidEndDragging:willDecelerate: method, assuming willDecelerate was YES. On Mac OS, this method never fires when using the scrollwheel because Mac OS implements its own inertia mechanism for scrolling.
+
+ - (void)carouselDidEndDecelerating:(iCarousel *)carousel;
+
+This method is called when the carousel finishes decelerating and you can assume that the currentItemIndex at this point is the final stopping value. Note however that even though it has stopped decelerating, the carousel will still scroll automatically until it aligns exactly on the current index. If you need to know when it has stopped moving completely, use the carouselDidEndScrollingAnimation delegate method.
+
- (float)carouselItemWidth:(iCarousel *)carousel;
Returns the width of each item in the carousel - i.e. the spacing for each item view. If the method is not implemented, this defaults to the width of the first item view that is returned by the `carousel:viewForItemAtIndex:` method.
View
5 RELEASE NOTES.md
@@ -1,3 +1,8 @@
+Version 1.3.3
+
+- Added several additional delegate methods for tracking when the carousel scrolls and decelerates
+- Fixed some glitches when using the scrollwheel/trackpad on a Mac.
+
Version 1.3.2
- Fixed additional scrolling bug introduced by 1.3.1 fix.
View
30 iCarousel Mac Demo/iCarouselMac/iCarouselWindowController.m
@@ -160,4 +160,34 @@ - (BOOL)carouselShouldWrap:(iCarousel *)carousel
return wrap;
}
+- (void)carouselWillBeginDragging:(iCarousel *)carousel
+{
+ NSLog(@"Carousel will begin dragging");
+}
+
+- (void)carouselDidEndDragging:(iCarousel *)carousel willDecelerate:(BOOL)decelerate
+{
+ NSLog(@"Carousel did end dragging and %@ decelerate", decelerate? @"will": @"won't");
+}
+
+- (void)carouselWillBeginDecelerating:(iCarousel *)carousel
+{
+ NSLog(@"Carousel will begin decelerating");
+}
+
+- (void)carouselDidEndDecelerating:(iCarousel *)carousel
+{
+ NSLog(@"Carousel did end decelerating");
+}
+
+- (void)carouselWillBeginScrollingAnimation:(iCarousel *)carousel
+{
+ NSLog(@"Carousel will begin scrolling");
+}
+
+- (void)carouselDidEndScrollingAnimation:(iCarousel *)carousel
+{
+ NSLog(@"Carousel did end scrolling");
+}
+
@end
View
30 iCarousel iOS Demo/iCarouselExampleViewController.m
@@ -204,6 +204,36 @@ - (BOOL)carouselShouldWrap:(iCarousel *)carousel
return wrap;
}
+- (void)carouselWillBeginDragging:(iCarousel *)carousel
+{
+ NSLog(@"Carousel will begin dragging");
+}
+
+- (void)carouselDidEndDragging:(iCarousel *)carousel willDecelerate:(BOOL)decelerate
+{
+ NSLog(@"Carousel did end dragging and %@ decelerate", decelerate? @"will": @"won't");
+}
+
+- (void)carouselWillBeginDecelerating:(iCarousel *)carousel
+{
+ NSLog(@"Carousel will begin decelerating");
+}
+
+- (void)carouselDidEndDecelerating:(iCarousel *)carousel
+{
+ NSLog(@"Carousel did end decelerating");
+}
+
+- (void)carouselWillBeginScrollingAnimation:(iCarousel *)carousel
+{
+ NSLog(@"Carousel will begin scrolling");
+}
+
+- (void)carouselDidEndScrollingAnimation:(iCarousel *)carousel
+{
+ NSLog(@"Carousel did end scrolling");
+}
+
- (void)carousel:(iCarousel *)_carousel didSelectItemAtIndex:(NSInteger)index
{
if (index == carousel.currentItemIndex)
View
9 iCarousel/iCarousel.h
@@ -1,7 +1,7 @@
//
// iCarousel.h
//
-// Version 1.3.2
+// Version 1.3.3
//
// Created by Nick Lockwood on 01/04/2011.
// Copyright 2010 Charcoal Design. All rights reserved.
@@ -91,6 +91,7 @@ iCarouselType;
float previousTranslation;
BOOL centerItemWhenSelected;
BOOL shouldWrap;
+ BOOL dragging;
}
#endif
@@ -145,8 +146,14 @@ iCarouselType;
@optional
+- (void)carouselWillBeginScrollingAnimation:(iCarousel *)carousel;
+- (void)carouselDidEndScrollingAnimation:(iCarousel *)carousel;
- (void)carouselDidScroll:(iCarousel *)carousel;
- (void)carouselCurrentItemIndexUpdated:(iCarousel *)carousel;
+- (void)carouselWillBeginDragging:(iCarousel *)carousel;
+- (void)carouselDidEndDragging:(iCarousel *)carousel willDecelerate:(BOOL)decelerate;
+- (void)carouselWillBeginDecelerating:(iCarousel *)carousel;
+- (void)carouselDidEndDecelerating:(iCarousel *)carousel;
- (float)carouselItemWidth:(iCarousel *)carousel;
- (BOOL)carouselShouldWrap:(iCarousel *)carousel;
- (CATransform3D)carousel:(iCarousel *)carousel transformForItemView:(UIView *)view withOffset:(float)offset;
View
98 iCarousel/iCarousel.m
@@ -1,7 +1,7 @@
//
// iCarousel.m
//
-// Version 1.3.2
+// Version 1.3.3
//
// Created by Nick Lockwood on 01/04/2011.
// Copyright 2010 Charcoal Design. All rights reserved.
@@ -73,6 +73,7 @@ @interface iCarousel ()
@property (nonatomic, assign) BOOL decelerating;
@property (nonatomic, assign) float previousTranslation;
@property (nonatomic, assign) BOOL shouldWrap;
+@property (nonatomic, assign) BOOL dragging;
- (void)layOutItemViews;
- (NSInteger)clampedIndex:(NSInteger)index;
@@ -112,6 +113,7 @@ @implementation iCarousel
@synthesize scrolling;
@synthesize previousTranslation;
@synthesize shouldWrap;
+@synthesize dragging;
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
@@ -595,6 +597,10 @@ - (void)scrollByNumberOfItems:(NSInteger)itemCount duration:(NSTimeInterval)dura
{
endOffset = [self clampedOffset:endOffset];
}
+ if ([delegate respondsToSelector:@selector(carouselWillBeginScrollingAnimation:)])
+ {
+ [delegate carouselWillBeginScrollingAnimation:self];
+ }
}
else
{
@@ -727,6 +733,18 @@ - (void)didScroll
}
}
+- (BOOL)decelerationEnded
+{
+ if (fabs(currentVelocity) >= itemWidth*0.5)
+ {
+ return NO;
+ }
+
+ float offset = [self minScrollDistanceFromOffset:self.currentItemIndex*itemWidth
+ toOffset:[self clampedOffset:scrollOffset]];
+ return fabs(offset) <= itemWidth*0.5;
+}
+
- (void)step
{
NSTimeInterval currentTime = CACurrentMediaTime();
@@ -741,27 +759,32 @@ - (void)step
time = 1.0;
scrolling = NO;
[self depthSortViews];
+ if ([delegate respondsToSelector:@selector(carouselDidEndScrollingAnimation:)])
+ {
+ [delegate carouselDidEndScrollingAnimation:self];
+ }
}
float delta = (time < 0.5f)? 0.5f * pow(time * 2.0, 3.0): 0.5f * pow(time * 2.0 - 2.0, 3.0) + 1.0; //ease in/out
scrollOffset = startOffset + (endOffset - startOffset) * delta;
[self didScroll];
}
else if (decelerating)
{
- float index = self.currentItemIndex;
- float offset = [self minScrollDistanceFromOffset:index*itemWidth toOffset:[self clampedOffset:scrollOffset]];
-
currentVelocity *= decelerationRate;
if (!shouldWrap && (scrollOffset < 0 || scrollOffset > (numberOfItems - 1) * itemWidth))
{
//decelerate faster if out of bounds
currentVelocity *= decelerationRate * decelerationRate;
}
scrollOffset -= currentVelocity * deltaTime;
- if (fabs(currentVelocity) < itemWidth*0.5 && fabs(offset) < itemWidth*0.5)
+ if ([self decelerationEnded])
{
decelerating = NO;
- [self scrollToItemAtIndex:index animated:YES];
+ if ([delegate respondsToSelector:@selector(carouselDidEndDecelerating:)])
+ {
+ [delegate carouselDidEndDecelerating:self];
+ }
+ [self scrollToItemAtIndex:self.currentItemIndex animated:YES];
}
[self didScroll];
}
@@ -797,15 +820,34 @@ - (void)didPan:(UIPanGestureRecognizer *)panGesture
{
case UIGestureRecognizerStateBegan:
{
+ dragging = YES;
scrolling = NO;
decelerating = NO;
previousTranslation = [panGesture translationInView:self].x;
+ if ([delegate respondsToSelector:@selector(carouselWillBeginDragging:)])
+ {
+ [delegate carouselWillBeginDragging:self];
+ }
break;
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
{
- decelerating = YES;
+ dragging = NO;
+ decelerating = ![self decelerationEnded];
+ if ([delegate respondsToSelector:@selector(carouselDidEndDragging:willDecelerate:)])
+ {
+ [delegate carouselDidEndDragging:self willDecelerate:decelerating];
+ }
+ if (!decelerating)
+ {
+ [self scrollToItemAtIndex:self.currentItemIndex animated:YES];
+ }
+ else if ([delegate respondsToSelector:@selector(carouselWillBeginDecelerating:)])
+ {
+ [delegate carouselWillBeginDecelerating:self];
+ }
+ break;
}
default:
{
@@ -842,6 +884,14 @@ - (void)mouseDragged:(NSEvent *)theEvent
{
if (scrollEnabled)
{
+ if (!dragging)
+ {
+ dragging = YES;
+ if ([delegate respondsToSelector:@selector(carouselWillBeginDragging:)])
+ {
+ [delegate carouselWillBeginDragging:self];
+ }
+ }
scrolling = NO;
decelerating = NO;
@@ -854,17 +904,47 @@ - (void)mouseDragged:(NSEvent *)theEvent
startTime = thisTime;
scrollOffset -= translation * factor;
[self didScroll];
-
- decelerating = YES;
}
}
+- (void)mouseUp:(NSEvent *)theEvent
+{
+ if (scrollEnabled)
+ {
+ dragging = NO;
+ decelerating = ![self decelerationEnded];
+ if ([delegate respondsToSelector:@selector(carouselDidEndDragging:willDecelerate:)])
+ {
+ [delegate carouselDidEndDragging:self willDecelerate:decelerating];
+ }
+ if (!decelerating)
+ {
+ [self scrollToItemAtIndex:self.currentItemIndex animated:YES];
+ }
+ else if ([delegate respondsToSelector:@selector(carouselWillBeginDecelerating:)])
+ {
+ [delegate carouselWillBeginDecelerating:self];
+ }
+ }
+}
+
#pragma mark -
#pragma mark Scrollwheel control
- (void)scrollWheel:(NSEvent *)theEvent
{
[self mouseDragged:theEvent];
+
+ //the iCarousel deceleration system conflicts with the built-in momentum
+ //scrolling for scrollwheel events. need to find a way to trigger the appropriate
+ //events, and also detect when user has disabled momentum scrolling in system prefs
+ dragging = NO;
+ decelerating = NO;
+ if ([delegate respondsToSelector:@selector(carouselDidEndDragging:willDecelerate:)])
+ {
+ [delegate carouselDidEndDragging:self willDecelerate:decelerating];
+ }
+ [self scrollToItemAtIndex:self.currentItemIndex animated:YES];
}
#pragma mark -

0 comments on commit 3d70935

Please sign in to comment.
Something went wrong with that request. Please try again.