From a433398d4331d6f4a7bf077d83f66295ce649edf Mon Sep 17 00:00:00 2001 From: Aditya Nrusimha Date: Tue, 25 Sep 2012 18:18:55 -0700 Subject: [PATCH] Added an example for TUIControl tracking with sliding tabs! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's pretty fun to play with sliding tabs…. :D --- ExampleProject/Example/ExampleTabBar.m | 86 +++++++++++++++++++++----- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/ExampleProject/Example/ExampleTabBar.m b/ExampleProject/Example/ExampleTabBar.m index cb0c17f..65117ef 100644 --- a/ExampleProject/Example/ExampleTabBar.m +++ b/ExampleProject/Example/ExampleTabBar.m @@ -16,35 +16,89 @@ #import "ExampleTabBar.h" -@interface ExampleTab : TUIView +@interface ExampleTab : TUIControl + +@property (nonatomic, assign) CGFloat originalPosition; + @end + @implementation ExampleTab -- (ExampleTabBar *)tabBar -{ +// Convinience to call delegate methods, since by default, our +// superview SHOULD be a tab bar, and nothing else. This will +// horribly crash if you add an ExampleTab to anything but a tab bar. +- (ExampleTabBar *)tabBar { return (ExampleTabBar *)self.superview; } -- (void)mouseDown:(NSEvent *)event -{ - [super mouseDown:event]; // always call super when overriding mouseXXX: methods - lots of plumbing happens in TUIView +// If a tracking event occurs within a tab, we want to move the +// tab around, so return YES. +- (BOOL)beginTrackingWithEvent:(NSEvent *)event { [self setNeedsDisplay]; + + // So the tab doesn't move over just on mouse pressed. + self.originalPosition = [self convertPoint:[event locationInWindow] fromView:nil].x; + + return YES; } -- (void)mouseUp:(NSEvent *)event -{ - [super mouseUp:event]; +// Find the tab that was being dragged- although this isn't the +// most efficient way, this will suffice for now. +- (BOOL)continueTrackingWithEvent:(NSEvent *)event { - // rather than a simple -setNeedsDisplay, let's fade it back out - [TUIView animateWithDuration:0.5 animations:^{ - [self redraw]; // -redraw forces a .contents update immediately based on drawRect, and it happens inside an animation block, so CoreAnimation gives us a cross-fade for free + // Offset the tab's x origin by whatever we dragged by. + // Animating it makes it fun if the drag goes pretty far. + CGFloat currentPosition = [self convertPoint:[event locationInWindow] fromView:nil].x; + [TUIView animateWithDuration:0.1 animations:^{ + + // Setting an ease-in-out animation curve slows down + // the animation at the start and finish, but speeds it up + // during the middle. Just for that "don't slide me!" fun. + [TUIView setAnimationCurve:TUIViewAnimationCurveEaseInOut]; + + CGRect draggedRect = self.frame; + draggedRect.origin.x += roundf(currentPosition - self.originalPosition); + self.frame = draggedRect; }]; - if([self eventInside:event]) { // only perform the action if the mouse up happened inside our bounds - ignores mouse down, drag-out, mouse up - [[self tabBar].delegate tabBar:[self tabBar] didSelectTab:self.tag]; - } + return YES; } +// Restore tabs to their original condition. +- (void)endTrackingWithEvent:(NSEvent *)event { + + // By nature, the event *must* be inside the tab's bounds, otherwise the + // tracking process will not be invoked. If we were dragged, we were NOT + // pressed, so don't call the delegate. + CGFloat currentPosition = [self convertPoint:[event locationInWindow] fromView:nil].x; + if(self.originalPosition == currentPosition) + [[self tabBar].delegate tabBar:[self tabBar] didSelectTab:self.tag]; + + // Since tracking is done, move the tab back. This whole ordeal lets us + // "stretch" our tabs around. + float originalPoint = self.tag * (self.tabBar.bounds.size.width / self.tabBar.tabViews.count); + [TUIView animateWithDuration:0.2 animations:^{ + CGRect draggedRect = self.frame; + draggedRect.origin.x = roundf(originalPoint); + self.frame = draggedRect; + }]; + + // Rather than a simple -setNeedsDisplay, let's fade it back out. + [TUIView animateWithDuration:0.3 animations:^{ + + // -redraw forces a .contents update immediately based on drawRect, + // and it happens inside an animation block, so CoreAnimation gives + // us a cross-fade for free. + [self redraw]; + }]; +} + +@end + +@interface ExampleTabBar () + +@property (nonatomic, assign) ExampleTab *draggingTab; + @end @implementation ExampleTabBar @@ -61,7 +115,7 @@ t.tag = i; t.layout = ^(TUIView *v) { // the layout of an individual tab is a function of the superview bounds, the number of tabs, and the current tab index CGRect b = v.superview.bounds; // reference the passed-in 'v' rather than 't' to avoid a retain cycle - float width = b.size.width / nTabs; + float width = (b.size.width / nTabs); float x = i * width; return CGRectMake(roundf(x), 0, roundf(width), b.size.height); };