From b19d79a136c7b252cd7fc1e12b13d7681848838a Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Tue, 1 Apr 2014 23:20:46 +0200 Subject: [PATCH] Adds NGDragGestureRecognizer and MainViewController which demos it. --- .../project.pbxproj | 6 ++ DragGestureRecognizer/MainViewController.m | 68 +++++++++------ .../NGDragGestureRecognizer.h | 48 +++++++++++ .../NGDragGestureRecognizer.m | 84 +++++++++++++++++++ 4 files changed, 182 insertions(+), 24 deletions(-) create mode 100644 DragGestureRecognizer/NGDragGestureRecognizer.h create mode 100644 DragGestureRecognizer/NGDragGestureRecognizer.m diff --git a/DragGestureRecognizer.xcodeproj/project.pbxproj b/DragGestureRecognizer.xcodeproj/project.pbxproj index fe0255a..139a5e5 100644 --- a/DragGestureRecognizer.xcodeproj/project.pbxproj +++ b/DragGestureRecognizer.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 2EBB3DBE18EB638C007A7E63 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2EBB3DBC18EB638C007A7E63 /* InfoPlist.strings */; }; 2EBB3DC018EB638C007A7E63 /* DragGestureRecognizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBB3DBF18EB638C007A7E63 /* DragGestureRecognizerTests.m */; }; 2EBB3DCB18EB63E2007A7E63 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBB3DCA18EB63E2007A7E63 /* MainViewController.m */; }; + 2EBB3DCE18EB64DB007A7E63 /* NGDragGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EBB3DCD18EB64DB007A7E63 /* NGDragGestureRecognizer.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -51,6 +52,8 @@ 2EBB3DBF18EB638C007A7E63 /* DragGestureRecognizerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DragGestureRecognizerTests.m; sourceTree = ""; }; 2EBB3DC918EB63E2007A7E63 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; }; 2EBB3DCA18EB63E2007A7E63 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; }; + 2EBB3DCC18EB64DB007A7E63 /* NGDragGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NGDragGestureRecognizer.h; sourceTree = ""; }; + 2EBB3DCD18EB64DB007A7E63 /* NGDragGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NGDragGestureRecognizer.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -114,6 +117,8 @@ 2EBB3DAA18EB638C007A7E63 /* AppDelegate.m */, 2EBB3DAC18EB638C007A7E63 /* Images.xcassets */, 2EBB3DA118EB638C007A7E63 /* Supporting Files */, + 2EBB3DCC18EB64DB007A7E63 /* NGDragGestureRecognizer.h */, + 2EBB3DCD18EB64DB007A7E63 /* NGDragGestureRecognizer.m */, 2EBB3DC918EB63E2007A7E63 /* MainViewController.h */, 2EBB3DCA18EB63E2007A7E63 /* MainViewController.m */, ); @@ -246,6 +251,7 @@ files = ( 2EBB3DAB18EB638C007A7E63 /* AppDelegate.m in Sources */, 2EBB3DCB18EB63E2007A7E63 /* MainViewController.m in Sources */, + 2EBB3DCE18EB64DB007A7E63 /* NGDragGestureRecognizer.m in Sources */, 2EBB3DA718EB638C007A7E63 /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/DragGestureRecognizer/MainViewController.m b/DragGestureRecognizer/MainViewController.m index 4fdbe92..07629cd 100644 --- a/DragGestureRecognizer/MainViewController.m +++ b/DragGestureRecognizer/MainViewController.m @@ -7,43 +7,63 @@ // #import "MainViewController.h" +#import "NGDragGestureRecognizer.h" + @interface MainViewController () +@property (strong, nonatomic) UIView * containerView; +@property (strong, nonatomic) UIView * draggableView; + @end -@implementation MainViewController -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil -{ - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - if (self) { - // Custom initialization - } - return self; -} +@implementation MainViewController - (void)viewDidLoad { [super viewDidLoad]; - // Do any additional setup after loading the view. + + CGFloat diameter = 44; + + for (NSInteger row = 1; row < 5; row++) { + for (NSInteger column = 1; column < 4; column++) { + + UIView * view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, diameter, diameter)]; + view.center = CGPointMake(column * 80, row * 96); + view.backgroundColor = [UIColor whiteColor]; + view.layer.cornerRadius = diameter / 2; + view.layer.borderColor = [UIColor colorWithRed:0.0902 green:0.5765 blue:0.7725 alpha:1.0000].CGColor; + view.layer.borderWidth = 3; + + NGDragGestureRecognizer * gestureRecognizer = [[NGDragGestureRecognizer alloc] initWithTarget:self action:@selector(handleDragGesture:)]; + [view addGestureRecognizer:gestureRecognizer]; + [self.view addSubview:view]; + } + } } -- (void)didReceiveMemoryWarning +- (void)handleDragGesture:(NGDragGestureRecognizer *)gestureRecognizer { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. + UIView * draggedView = gestureRecognizer.view; + UIView * superview = draggedView.superview; + + if (gestureRecognizer.state == UIGestureRecognizerStateBegan) { + [gestureRecognizer setTranslation:draggedView.center inView:superview]; + [superview bringSubviewToFront:draggedView]; + draggedView.layer.borderColor = [UIColor colorWithRed:0.2784 green:0.7412 blue:0.9412 alpha:1.0000].CGColor; + return; + } + + if (gestureRecognizer.state == UIGestureRecognizerStateChanged) { + draggedView.center = [gestureRecognizer translationInView:superview]; + return; + } + + if (gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled) { + draggedView.layer.borderColor = [UIColor colorWithRed:0.0902 green:0.5765 blue:0.7725 alpha:1.0000].CGColor; + return; + } } -/* -#pragma mark - Navigation - -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender -{ - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. -} -*/ - @end diff --git a/DragGestureRecognizer/NGDragGestureRecognizer.h b/DragGestureRecognizer/NGDragGestureRecognizer.h new file mode 100644 index 0000000..3c8c250 --- /dev/null +++ b/DragGestureRecognizer/NGDragGestureRecognizer.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014 Wojciech Nagrodzki + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import + + +@interface NGDragGestureRecognizer : UILongPressGestureRecognizer + +/** + * The translation of the pan gesture in the coordinate system of the specified view. + * The x and y values report the total translation over time. They are not delta values from the last time that the translation was reported. + * Apply the translation value to the state of the view when the gesture is first recognized—do not concatenate the value each time the handler is called. + * + * @param view The view in whose coordinate system the translation of the long press pan gesture should be computed. + If you want to adjust a view's location to keep it under the user's finger, request the translation in that view's superview's coordinate system. + * @return A point identifying the new location of a view in the coordinate system of its designated superview. + */ +- (CGPoint)translationInView:(UIView *)view; + +/** + * Sets the translation value in the coordinate system of the specified view. + * Changing the translation value resets the velocity of the pan. + * + * @param translation A point that identifies the new translation value. + * @param view A view in whose coordinate system the translation is to occur. + */ +- (void)setTranslation:(CGPoint)translation inView:(UIView *)view; + +@end diff --git a/DragGestureRecognizer/NGDragGestureRecognizer.m b/DragGestureRecognizer/NGDragGestureRecognizer.m new file mode 100644 index 0000000..d139802 --- /dev/null +++ b/DragGestureRecognizer/NGDragGestureRecognizer.m @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014 Wojciech Nagrodzki + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import "NGDragGestureRecognizer.h" +#import + + +@interface NGDragGestureRecognizer () + +@property (assign, nonatomic) CGPoint initialTouchLocation; +@property (strong, nonatomic) NSMapTable * viewsBeginLocations; + +@end + + +@implementation NGDragGestureRecognizer + +#pragma mark - Public Instance Methods + +- (CGPoint)translationInView:(UIView *)view +{ + CGPoint currentLocation = [self locationInView:view]; + NSValue * initialLocationValue = [self.viewsBeginLocations objectForKey:view]; + CGPoint initialLocation = initialLocationValue ? initialLocationValue.CGPointValue : [[self referenceView] convertPoint:self.initialTouchLocation toView:view]; + return CGPointMake(currentLocation.x - initialLocation.x, currentLocation.y - initialLocation.y); +} + +- (void)setTranslation:(CGPoint)translation inView:(UIView *)view +{ + CGPoint currentLocation = [self locationInView:view]; + CGPoint initialLocation = CGPointMake(currentLocation.x - translation.x, currentLocation.y - translation.y); + NSValue * initialLocationValue = [NSValue valueWithCGPoint:initialLocation]; + [self.viewsBeginLocations setObject:initialLocationValue forKey:view]; +} + +#pragma mark - Overriden + +- (void)setState:(UIGestureRecognizerState)state +{ + [super setState:state]; + + if (state == UIGestureRecognizerStateBegan) { + [self.viewsBeginLocations removeAllObjects]; + self.initialTouchLocation = [self locationInView:[self referenceView]]; + } +} + +#pragma mark - Private Instance Methods + +- (NSMapTable *)viewsBeginLocations +{ + if (_viewsBeginLocations == nil) { + _viewsBeginLocations = [NSMapTable weakToStrongObjectsMapTable]; + } + return _viewsBeginLocations; +} + +#pragma mark - Private Methods + +- (UIView *)referenceView +{ + return self.view; +} + +@end