Created
November 3, 2014 18:25
-
-
Save marchold/a4348ecd7978194d39e9 to your computer and use it in GitHub Desktop.
A resizable image view, never fully worked but the affine transform stuff in it is solid
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <UIKit/UIKit.h> | |
CGFloat CGAffineTransformGetScaleX(CGAffineTransform t); | |
CGFloat CGAffineTransformGetScaleY(CGAffineTransform t); | |
CGFloat CGAffineTransformGetRotation(CGAffineTransform t); | |
CGFloat CGAffineTransformGetTranslationX(CGAffineTransform t); | |
CGFloat CGAffineTransformGetTranslationY(CGAffineTransform t); | |
CGAffineTransform CGAffineTransformFromRectToRect(CGRect fromRect, CGRect toRect); | |
CGAffineTransform CGAffineTransformFromRectToRectKeepAspectRatio( CGRect fromRect, CGRect toRect ); | |
CGPoint offsetPointToCoordinates(CGPoint parentCenterPoint, CGPoint pointToOffset); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "CGAffineTransform_Additions.h" | |
CGFloat CGAffineTransformGetScaleX(CGAffineTransform t) | |
{ | |
return sqrt(t.a * t.a + t.c * t.c); | |
} | |
CGFloat CGAffineTransformGetScaleY(CGAffineTransform t) | |
{ | |
return sqrt(t.b * t.b + t.d * t.d); | |
} | |
CGFloat CGAffineTransformGetRotation(CGAffineTransform t) | |
{ | |
return atan2f(t.b, t.a); | |
} | |
CGFloat CGAffineTransformGetTranslationX(CGAffineTransform t) | |
{ | |
return t.tx; | |
} | |
CGFloat CGAffineTransformGetTranslationY(CGAffineTransform t) | |
{ | |
return t.ty; | |
} | |
CGAffineTransform CGAffineTransformFromRectToRect(CGRect fromRect, CGRect toRect) | |
{ | |
CGAffineTransform trans1 = CGAffineTransformMakeTranslation(-fromRect.origin.x, -fromRect.origin.y); | |
CGAffineTransform scale = CGAffineTransformMakeScale(toRect.size.width/fromRect.size.width, toRect.size.height/fromRect.size.height); | |
CGAffineTransform trans2 = CGAffineTransformMakeTranslation(toRect.origin.x, toRect.origin.y); | |
return CGAffineTransformConcat(CGAffineTransformConcat(trans1, scale), trans2); | |
} | |
CGAffineTransform CGAffineTransformFromRectToRectKeepAspectRatio( CGRect fromRect, CGRect toRect ) | |
{ | |
float aspectRatio = fromRect.size.width / fromRect.size.height; | |
if( aspectRatio > ( toRect.size.width / toRect.size.height )) | |
{ | |
toRect = CGRectInset( toRect, 0, ( toRect.size.height - toRect.size.width / aspectRatio ) / 2.0f ); | |
} | |
else | |
{ | |
toRect = CGRectInset( toRect, ( toRect.size.width - toRect.size.height * aspectRatio ) / 2.0f, 0 ); | |
} | |
return CGAffineTransformFromRectToRect( fromRect, toRect ); | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "ResizableImageView.h" | |
#import "CGAffineTransform_Additions.h" | |
#define MAXIMUM_SCALE 2 | |
@interface ResizableImageView(){ | |
UIPanGestureRecognizer *panGesture; | |
BOOL enableOtherGestureRecognizer; | |
CGFloat minimumScale; | |
CGRect vcFrame; | |
} | |
@property (nonatomic,retain) UIImage *originalImage; | |
@property (nonatomic,retain) UIScrollView *scrollView; | |
@end | |
@implementation PLResizableImageView | |
-(id)initWithCoder:(NSCoder *)aDecoder | |
{ | |
self = [super initWithCoder:aDecoder]; | |
if (self) | |
{ | |
// [self initalize]; | |
} | |
return self; | |
} | |
-(id)initWithFrame:(CGRect)frame | |
{ | |
self = [super initWithFrame:frame]; | |
if (self) | |
{ | |
// [self initalize]; | |
} | |
return self; | |
} | |
-(CGRect)originalFrame | |
{ | |
CGAffineTransform t = self.transform; | |
self.transform = CGAffineTransformIdentity; | |
CGRect originalFrame = self.frame; | |
self.transform = t; | |
return originalFrame; | |
} | |
-(void)setImage:(UIImage *)image | |
{ | |
self.originalImage = image; | |
minimumScale = self.frame.size.width/image.size.width; | |
CGFloat verticalScale = self.frame.size.height/image.size.height; | |
if (minimumScale > verticalScale) minimumScale=verticalScale; | |
NSLog(@"Initial Scale = %f",minimumScale); | |
CGAffineTransform transform = CGAffineTransformMakeScale(minimumScale, minimumScale); | |
self.transform = transform; | |
[super setImage:image]; | |
} | |
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer | |
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer | |
{ | |
return YES; | |
} | |
-(BOOL)scrollViewShouldScroll{ | |
return (CGAffineTransformGetScaleX(self.transform) == minimumScale); | |
} | |
-(void) initalize:(UIScrollView*)scrollView vc:(UIViewController*)vc | |
{ | |
self.scrollView = scrollView; | |
self.userInteractionEnabled = YES; | |
self.contentMode = UIViewContentModeCenter; | |
self.clipsToBounds = YES; | |
enableOtherGestureRecognizer = YES; | |
// Pinch | |
UIPinchGestureRecognizer *pgr = [[UIPinchGestureRecognizer alloc] | |
initWithTarget:self action:@selector(pinch:)]; | |
pgr.delegate = self; | |
[self addGestureRecognizer:pgr]; | |
[self.scrollView addGestureRecognizer:pgr]; | |
// Pan | |
panGesture = [[UIPanGestureRecognizer alloc] | |
initWithTarget:self action:@selector(pan:)]; | |
panGesture.delegate = self; | |
//[self addGestureRecognizer:panGesture]; | |
[self.scrollView addGestureRecognizer:panGesture]; | |
// Double Tap | |
UITapGestureRecognizer *dtgr = [[UITapGestureRecognizer alloc] | |
initWithTarget:self action:@selector(doubleTap:)]; | |
dtgr.numberOfTapsRequired = 2; | |
dtgr.delegate = self; | |
[self addGestureRecognizer:dtgr]; | |
[self.scrollView addGestureRecognizer:dtgr]; | |
vcFrame = vc.view.frame; | |
[self.scrollView.panGestureRecognizer requireGestureRecognizerToFail:panGesture]; | |
} | |
-(CGPoint)offsetPointToCenterCoordinates:(CGPoint)aPoint | |
{ | |
return CGPointMake(aPoint.x+self.center.x, aPoint.y+self.center.y); | |
} | |
-(CGPoint)pointInViewCenterTerms:(CGPoint)aPoint | |
{ | |
return CGPointMake(aPoint.x-self.center.x, aPoint.y-self.center.y); | |
} | |
-(CGPoint)pointInTransformedView:(CGPoint)aPoint | |
{ | |
CGPoint offsetItem = [self pointInViewCenterTerms:aPoint]; | |
CGPoint updatedItem = CGPointApplyAffineTransform(offsetItem, self.transform); | |
return [self offsetPointToCenterCoordinates:updatedItem]; | |
} | |
- (void)pinch:(UIPinchGestureRecognizer *)gesture | |
{ | |
if ( gesture.state == UIGestureRecognizerStateEnded | |
|| gesture.state == UIGestureRecognizerStateChanged) | |
{ | |
self.transform = CGAffineTransformScale(self.transform, gesture.scale , gesture.scale ); | |
CGFloat scale = CGAffineTransformGetScaleX(self.transform); | |
if (scale < minimumScale) | |
{ | |
CGFloat x = CGAffineTransformGetTranslationX(self.transform); | |
CGFloat y = CGAffineTransformGetTranslationY(self.transform); | |
self.transform = CGAffineTransformIdentity; | |
self.transform = CGAffineTransformScale(self.transform, minimumScale, minimumScale ); | |
self.transform = CGAffineTransformTranslate(self.transform, x, y); | |
} | |
if (scale > MAXIMUM_SCALE) | |
{ | |
CGFloat x = CGAffineTransformGetTranslationX(self.transform); | |
CGFloat y = CGAffineTransformGetTranslationY(self.transform); | |
self.transform = CGAffineTransformIdentity; | |
self.transform = CGAffineTransformScale(self.transform, MAXIMUM_SCALE, MAXIMUM_SCALE ); | |
self.transform = CGAffineTransformTranslate(self.transform, x, y); | |
} | |
if (scale > minimumScale) | |
{ | |
// self.scrollView.scrollEnabled = NO; | |
} | |
} | |
} | |
- (void)pan:(UIPanGestureRecognizer *)gesture | |
{ | |
//Apply the translation from the gesture directly to our views transform matrix | |
CGPoint translation = [gesture translationInView:self]; | |
CGAffineTransform transform = CGAffineTransformTranslate(self.transform, translation.x, translation.y); | |
self.transform = transform; | |
[gesture setTranslation:CGPointMake(0, 0) inView:self]; | |
//In case we scrolled the image too far we need to translate the transform back | |
CGPoint topLeft = [self pointInTransformedView:self.originalFrame.origin]; | |
CGPoint bottomRight = [self pointInTransformedView:CGPointMake(self.originalFrame.origin.x+self.originalFrame.size.width, | |
self.originalFrame.origin.y+self.originalFrame.size.height)]; | |
if (topLeft.x > 0) | |
{ | |
transform = CGAffineTransformFromRectToRect(self.frame,CGRectMake(0, | |
self.frame.origin.y, | |
self.frame.size.width, | |
self.frame.size.height)); | |
self.transform = CGAffineTransformConcat(self.transform, transform); | |
} | |
if (topLeft.y > 0) | |
{ | |
transform = CGAffineTransformFromRectToRect(self.frame,CGRectMake(self.frame.origin.x, | |
0, | |
self.frame.size.width, | |
self.frame.size.height)); | |
self.transform = CGAffineTransformConcat(self.transform, transform); | |
} | |
if (bottomRight.x < vcFrame.size.width) | |
{ | |
transform = CGAffineTransformFromRectToRect(self.frame,CGRectMake(self.frame.origin.x+(vcFrame.size.width-bottomRight.x), | |
self.frame.origin.y, | |
self.frame.size.width, | |
self.frame.size.height)); | |
self.transform = CGAffineTransformConcat(self.transform, transform); | |
} | |
if (bottomRight.y < vcFrame.size.height) | |
{ | |
transform = CGAffineTransformFromRectToRect(self.frame,CGRectMake(self.frame.origin.x, | |
self.frame.origin.y+(vcFrame.size.height-bottomRight.y), | |
self.frame.size.width, | |
self.frame.size.height)); | |
self.transform = CGAffineTransformConcat(self.transform, transform); | |
} | |
} | |
- (void)doubleTap:(UITapGestureRecognizer *)gesture | |
{ | |
if(CGAffineTransformGetScaleX(self.transform) == minimumScale) | |
{ | |
CGAffineTransform transform = CGAffineTransformMakeScale(1, 1); | |
self.transform = transform; | |
// self.scrollView.scrollEnabled = NO; | |
} | |
else | |
{ | |
CGAffineTransform transform = CGAffineTransformMakeScale(minimumScale, minimumScale); | |
self.transform = transform; | |
// self.scrollView.scrollEnabled = YES; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I never did use this because it did not work well within a scroll view as I had hoped. I think this could be a good starting point for other matrix transform operations.