Permalink
Browse files

Initial commit.

  • Loading branch information...
0 parents commit 0c88b9ec5daa707e708d16c0d5cdbbdd5a466e33 @millenomi committed Mar 23, 2009
@@ -0,0 +1,7 @@
+.svn
+Build
+TemporaryItems
+*.xcodeproj/*.mode2v3
+*.xcodeproj/*.pbxuser
+.DS_Store
+Compiled Locales
@@ -0,0 +1,74 @@
+//
+// L0DraggableView.h
+// Shard
+//
+// Created by ∞ on 21/03/09.
+// Copyright 2009 __MyCompanyName__. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@protocol L0DraggableViewDelegate;
+
+@interface L0DraggableView : UIView {
+ BOOL _dragging;
+ CGPoint _lastLocation;
+
+ NSDate* _dragStartDate;
+ NSTimeInterval _lastSpeedRecordingIntervalSinceStartOfDrag;
+ CGPoint _lastSpeedRecordingLocation;
+
+ id <L0DraggableViewDelegate> delegate;
+ BOOL _delegateImplementsDidDragToPoint;
+}
+
+@property(assign) id <L0DraggableViewDelegate> delegate;
+
+@end
+
+@protocol L0DraggableViewDelegate <NSObject>
+
+@optional
+
+// Called before dragging starts. NO prevents dragging.
+- (BOOL) draggableViewShouldBeginDragging:(L0DraggableView*) view;
+
+// Called as dragging has started.
+- (void) draggableViewDidBeginDragging:(L0DraggableView*) view;
+
+// Called as the user moves the view to a given point.
+- (void) draggableView:(L0DraggableView*) view didDragToPoint:(CGPoint) point;
+
+// Called as the user takes the finger off the view. If slide is YES, the view will NOT
+// stop moving, as an inertial slide will begin instead.
+- (void) draggableViewDidEndDragging:(L0DraggableView*) view continuesWithSlide:(BOOL) slide;
+
+// Called as the view begins performing an inertial slide to a given point.
+// Call made within the animation block that performs the slide.
+- (void) draggableView:(L0DraggableView*) view willBeginIntertialSlideToPoint:(CGPoint) point;
+
+// Called as the view stops performing an inertial slide to a given point.
+// If finished == NO, the slide was interrupted (eg because the user started dragging
+// the view again).
+- (void) draggableView:(L0DraggableView*) view didEndInertialSlideByFinishing:(BOOL) finished;
+
+// Called to determine if there's an attraction point we want the view to move towards
+// at the end of a drag. "start" is the point where the draggable view would end up with no
+// attraction, either the point where it was left by the user or the slide's endpoint if
+// a flick initiates an inertial slide.
+// Note that interrupting a slide animation (eg by dragging the view again) prevents
+// attraction.
+
+// Works like this:
+// - If the user ends the drag still, it will move towards the attraction point with an ease-in-out curve.
+// - TODO: If the user ends the drag with a slide, <strike>and the slide's endpoint is one lenght or less away from the attraction point</strike>, the slide will move to the attraction point rather than the endpoint.
+// Currently, all interactions between an attraction point and a slide cause the attraction point to replace the slide's endpoint (so the above IS implemented, just not for some slides -- for all of them.)
+// TODO: - If the user ends the drag with a slide and the endpoint is more than one length away from the attraction point, it will slide towards the endpoint for about half of the way, then curve towards the attraction point.
+- (BOOL) draggableView:(L0DraggableView*) view shouldMoveFromPoint:(CGPoint) start toAttractionPoint:(CGPoint*) outPoint;
+
+// Called as an attraction point animation ends.
+// If finished == NO, the attraction was interrupted (eg because the user started dragging
+// the view again).
+- (void) draggableView:(L0DraggableView*) view didEndAttractionByFinishing:(BOOL) finished;
+
+@end
@@ -0,0 +1,247 @@
+//
+// L0DraggableView.m
+// Shard
+//
+// Created by ∞ on 21/03/09.
+// Copyright 2009 __MyCompanyName__. All rights reserved.
+//
+
+#import "L0DraggableView.h"
+
+#pragma mark -
+#pragma mark L0DraggableViewSlideContext definitions
+
+static inline CGFloat L0ClampToMaximumAbsolute(CGFloat value, CGFloat maximumAbs) {
+ maximumAbs = ABS(maximumAbs);
+
+ if (value > maximumAbs)
+ value = maximumAbs;
+ else if (value < -maximumAbs)
+ value = -maximumAbs;
+
+ return value;
+}
+
+static inline CGFloat L0ClampToMinimumAbsolute(CGFloat value, CGFloat maximumAbs) {
+ maximumAbs = ABS(maximumAbs);
+
+ if (value < maximumAbs && value > 0)
+ value = maximumAbs;
+ else if (value > -maximumAbs && value < 0)
+ value = -maximumAbs;
+
+ return value;
+}
+
+#pragma mark -
+#pragma mark L0DraggableView private methods
+
+@interface L0DraggableView ()
+
+@end
+
+#pragma mark -
+#pragma mark L0DraggableView itself
+
+@implementation L0DraggableView
+
+- (void)dealloc {
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark L0DraggableView dragging methods
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
+{
+ if (delegate && [delegate respondsToSelector:@selector(draggableViewShouldBeginDragging:)]) {
+ BOOL go = [delegate draggableViewShouldBeginDragging:self];
+ if (!go) return;
+ }
+
+ _dragging = YES;
+ _lastLocation = [[touches anyObject] locationInView:self.superview];
+ _lastSpeedRecordingLocation = _lastLocation;
+ _dragStartDate = [NSDate new];
+ _lastSpeedRecordingIntervalSinceStartOfDrag = 0;
+
+ self.center = self.center; // stops animation.
+
+ if (delegate && [delegate respondsToSelector:@selector(draggableViewDidBeginDragging:)])
+ [delegate draggableViewDidBeginDragging:self];
+
+ [self performSelector:@selector(_recordSpeed) withObject:nil afterDelay:0.05];
+}
+
+- (void) _recordSpeed;
+{
+ _lastSpeedRecordingLocation = _lastLocation;
+ _lastSpeedRecordingIntervalSinceStartOfDrag = -[_dragStartDate timeIntervalSinceNow];
+
+ L0Log(@"speed = %@, interval since start = %f", NSStringFromCGPoint(_lastSpeedRecordingLocation), _lastSpeedRecordingIntervalSinceStartOfDrag);
+
+ [self performSelector:@selector(_recordSpeed) withObject:nil afterDelay:0.2];
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
+{
+ if (!_dragging) return;
+
+ CGPoint newLocation = [[touches anyObject] locationInView:self.superview];
+
+ CGFloat deltaX = newLocation.x - _lastLocation.x;
+ CGFloat deltaY = newLocation.y - _lastLocation.y;
+
+ CGPoint center = self.center;
+ center.x += deltaX;
+ center.y += deltaY;
+
+ self.center = center;
+
+ if (delegate && _delegateImplementsDidDragToPoint)
+ [delegate draggableView:self didDragToPoint:center];
+
+ _lastLocation = newLocation;
+}
+
+#define kL0MinimumMovementSpeedIn100MSForSlide 7.0
+
+static inline BOOL L0VectorHasPointWithinAbsolute(CGPoint vector, CGFloat rangeAbs) {
+ return
+ ABS(vector.x) < rangeAbs && ABS(vector.y) < rangeAbs;
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
+{
+ _dragging = NO;
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_recordSpeed) object:nil];
+
+ NSAssert(self.superview != nil, @"No events should be received without a superview.");
+ CGPoint here = [[touches anyObject] locationInView:self.superview];
+
+ CGPoint movementVector;
+ movementVector.x = here.x - _lastSpeedRecordingLocation.x;
+ movementVector.y = here.y - _lastSpeedRecordingLocation.y;
+
+ NSTimeInterval movementTime = (-[_dragStartDate timeIntervalSinceNow]) - _lastSpeedRecordingIntervalSinceStartOfDrag;
+
+ CGPoint speedPointsPer100MS;
+ speedPointsPer100MS.x = (movementVector.x / movementTime) * 0.1;
+ speedPointsPer100MS.y = (movementVector.y / movementTime) * 0.1;
+
+ BOOL continuesWithSlide = !L0VectorHasPointWithinAbsolute(speedPointsPer100MS, kL0MinimumMovementSpeedIn100MSForSlide);
+
+ if (delegate && [delegate respondsToSelector:@selector(draggableViewDidEndDragging:continuesWithSlide:)])
+ [delegate draggableViewDidEndDragging:self continuesWithSlide:continuesWithSlide];
+
+ if (!continuesWithSlide) {
+ // Determine attraction.
+ if (delegate && [delegate respondsToSelector:@selector(draggableView:shouldMoveFromPoint:toAttractionPoint:)]) {
+
+ CGPoint to;
+ BOOL shouldMove = [delegate draggableView:self shouldMoveFromPoint:self.center toAttractionPoint:&to];
+
+ if (shouldMove) {
+ [UIView beginAnimations:nil context:NULL];
+ [UIView setAnimationDelegate:self];
+ [UIView setAnimationDidStopSelector:@selector(_attractionAnimation:didEndByFinishing:context:)];
+
+ [UIView setAnimationDuration:1.2];
+ [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
+ self.center = to;
+
+ [UIView commitAnimations];
+ }
+ }
+
+ // No slide? Return.
+ return;
+ }
+
+ // ===========
+ // == SLIDE ==
+ // ===========
+
+#define kL0DampeningFactor 0.5
+#define kL0MaximumSlideDistanceX 100.0
+#define kL0MaximumSlideDistanceY 300.0
+ CGPoint delta = movementVector;
+ int timeScale = 1;
+ while (!L0VectorHasPointWithinAbsolute(movementVector, 5.0)) {
+ movementVector.x *= kL0DampeningFactor;
+ movementVector.y *= kL0DampeningFactor;
+
+ delta.x += movementVector.x;
+ delta.y += movementVector.y;
+
+ timeScale++;
+
+ if (ABS(delta.x) > kL0MaximumSlideDistanceX ||
+ ABS(delta.y) > kL0MaximumSlideDistanceY)
+ break;
+ }
+
+ [UIView beginAnimations:nil context:NULL];
+ [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
+ [UIView setAnimationBeginsFromCurrentState:YES];
+ [UIView setAnimationDuration:movementTime * timeScale];
+
+ if (delegate) {
+ [UIView setAnimationDelegate:self];
+ [UIView setAnimationDidStopSelector:@selector(_slideAnimation:didEndByFinishing:context:)];
+ }
+
+ CGPoint center = self.center;
+ center.x += delta.x;
+ center.y += delta.y;
+
+ // ~~~~~~~~~~~~~~~
+
+ if (delegate && [delegate respondsToSelector:@selector(draggableView:willBeginIntertialSlideToPoint:)])
+ [delegate draggableView:self willBeginIntertialSlideToPoint:center];
+
+
+ if (delegate && [delegate respondsToSelector:@selector(draggableView:shouldMoveFromPoint:toAttractionPoint:)]) {
+ CGPoint to;
+ BOOL shouldMove = [delegate draggableView:self shouldMoveFromPoint:self.center toAttractionPoint:&to];
+
+ if (shouldMove) {
+ // TODO two-part "curve" animation for long attractions
+ center = to;
+ }
+ }
+
+ self.center = center;
+
+ [UIView commitAnimations];
+}
+
+- (void) _slideAnimation:(NSString*) name didEndByFinishing:(BOOL) finished context:(void*) nothing;
+{
+ if (delegate && [delegate respondsToSelector:@selector(draggableView:didEndInertialSlideByFinishing:)])
+ [delegate draggableView:self didEndInertialSlideByFinishing:finished];
+}
+
+- (void) _attractionAnimation:(NSString*) name didEndByFinishing:(BOOL) finished context:(void*) nothing;
+{
+ if (delegate && [delegate respondsToSelector:@selector(draggableView:didEndAttractionByFinishing:)])
+ [delegate draggableView:self didEndAttractionByFinishing:finished];
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
+{
+ if (_dragging) {
+ _dragging = NO;
+ if (delegate && [delegate respondsToSelector:@selector(draggableViewDidEndDragging:continuesWithSlide:)])
+ [delegate draggableViewDidEndDragging:self continuesWithSlide:NO];
+ }
+}
+
+@synthesize delegate;
+- (void) setDelegate:(id <L0DraggableViewDelegate>) d;
+{
+ delegate = d;
+ _delegateImplementsDidDragToPoint = [d respondsToSelector:@selector(draggableView:didDragToPoint:)];
+}
+
+@end
@@ -0,0 +1,16 @@
+//
+// L0URLDetection.h
+// L0EntryTable
+//
+// Created by ∞ on 18/07/08.
+// Copyright 2008 Emanuele Vulcano. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+// returns YES if it's desirable to display the URL in an inline
+// web browsing component (that is, without leaving your application);
+// NO if the URL should be opened via -[UIApplication openURL:] (to
+// launch an appropriate application such as YouTube, App Store or the
+// like).
+extern BOOL L0ShouldNavigateToURL(NSURL* url);
Oops, something went wrong.

0 comments on commit 0c88b9e

Please sign in to comment.