https://github.com/spoletto/SPUserResizableView
HOW TO USE:

SPUserResizableView.h
HOW TO USE:
CGRect imageFrame = CGRectMake(50, 200, 100, 80);
SPUserResizableView *imageResizableView = [[SPUserResizableView alloc] initWithFrame:imageFrame];
CustomView *otherViewObj = [[CustomView alloc]initWithFrame:gripFrame];
imageResizableView.contentView = otherViewObj; // ADD ANY VIEW LIKE UIIMAGEVIEW ETC.
imageResizableView.delegate = self;
[self.view addSubview:imageResizableView];

SPUserResizableView.h
// // SPUserResizableView.h // SPUserResizableView // // Created by Stephen Poletto on 12/10/11. // // SPUserResizableView is a user-resizable, user-repositionable // UIView subclass. #importtypedef struct SPUserResizableViewAnchorPoint { CGFloat adjustsX; CGFloat adjustsY; CGFloat adjustsH; CGFloat adjustsW; } SPUserResizableViewAnchorPoint; @protocol SPUserResizableViewDelegate; @class SPGripViewBorderView; @interface SPUserResizableView : UIView { SPGripViewBorderView *borderView; UIView *contentView; CGPoint touchStart; CGFloat minWidth; CGFloat minHeight; // Used to determine which components of the bounds we'll be modifying, based upon where the user's touch started. SPUserResizableViewAnchorPoint anchorPoint; id delegate; } @property (nonatomic, assign) id delegate; // Will be retained as a subview. @property (nonatomic, assign) UIView *contentView; // Default is 48.0 for each. @property (nonatomic) CGFloat minWidth; @property (nonatomic) CGFloat minHeight; // Defaults to YES. Disables the user from dragging the view outside the parent view's bounds. @property (nonatomic) BOOL preventsPositionOutsideSuperview; - (void)hideEditingHandles; - (void)showEditingHandles; @end @protocol SPUserResizableViewDelegate @optional // Called when the resizable view receives touchesBegan: and activates the editing handles. - (void)userResizableViewDidBeginEditing:(SPUserResizableView *)userResizableView; // Called when the resizable view receives touchesEnded: or touchesCancelled: - (void)userResizableViewDidEndEditing:(SPUserResizableView *)userResizableView; @end
********************
//// SPUserResizableView.m// SPUserResizableView////#import "SPUserResizableView.h"/* Let's inset everything that's drawn (the handles and the content view)so that users can trigger a resize from a few pixels outside ofwhat they actually see as the bounding box. */#define kSPUserResizableViewGlobalInset 5.0#define kSPUserResizableViewDefaultMinWidth 48.0#define kSPUserResizableViewDefaultMinHeight 48.0#define kSPUserResizableViewInteractiveBorderSize 10.0static SPUserResizableViewAnchorPoint SPUserResizableViewNoResizeAnchorPoint = { 0.0, 0.0, 0.0, 0.0 };static SPUserResizableViewAnchorPoint SPUserResizableViewUpperLeftAnchorPoint = { 1.0, 1.0, -1.0, 1.0 };static SPUserResizableViewAnchorPoint SPUserResizableViewMiddleLeftAnchorPoint = { 1.0, 0.0, 0.0, 1.0 };static SPUserResizableViewAnchorPoint SPUserResizableViewLowerLeftAnchorPoint = { 1.0, 0.0, 1.0, 1.0 };//static SPUserResizableViewAnchorPoint SPUserResizableViewUpperMiddleAnchorPoint = { 0.0, 1.0, -1.0, 0.0 };static SPUserResizableViewAnchorPoint SPUserResizableViewUpperRightAnchorPoint = { 0.0, 1.0, -1.0, -1.0 };static SPUserResizableViewAnchorPoint SPUserResizableViewMiddleRightAnchorPoint = { 0.0, 0.0, 0.0, -1.0 };static SPUserResizableViewAnchorPoint SPUserResizableViewLowerRightAnchorPoint = { 0.0, 0.0, 1.0, -1.0 };//static SPUserResizableViewAnchorPoint SPUserResizableViewLowerMiddleAnchorPoint = { 0.0, 0.0, 1.0, 0.0 };@interface SPGripViewBorderView : UIView@end@implementation SPGripViewBorderView- (id)initWithFrame:(CGRect)frame {if ((self = [super initWithFrame:frame])) {// Clear background to ensure the content view shows through.self.backgroundColor = [UIColor clearColor];}return self;}- (void)drawRect:(CGRect)rect {CGContextRef context = UIGraphicsGetCurrentContext();CGContextSaveGState(context);// (1) Draw the bounding box.CGContextSetLineWidth(context, 1.0);CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);CGContextAddRect(context, CGRectInset(self.bounds, kSPUserResizableViewInteractiveBorderSize/2, kSPUserResizableViewInteractiveBorderSize/2));CGContextStrokePath(context);// (2) Calculate the bounding boxes for each of the anchor points.CGRect upperLeft = CGRectMake(0.0, 0.0, kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize);CGRect upperRight = CGRectMake(self.bounds.size.width - kSPUserResizableViewInteractiveBorderSize, 0.0, kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize);CGRect lowerRight = CGRectMake(self.bounds.size.width - kSPUserResizableViewInteractiveBorderSize, self.bounds.size.height - kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize);CGRect lowerLeft = CGRectMake(0.0, self.bounds.size.height - kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize);//CGRect upperMiddle = CGRectMake((self.bounds.size.width - kSPUserResizableViewInteractiveBorderSize)/2, 0.0, kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize);//CGRect lowerMiddle = CGRectMake((self.bounds.size.width - kSPUserResizableViewInteractiveBorderSize)/2, self.bounds.size.height - kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize);CGRect middleLeft = CGRectMake(0.0, (self.bounds.size.height - kSPUserResizableViewInteractiveBorderSize)/2, kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize);CGRect middleRight = CGRectMake(self.bounds.size.width - kSPUserResizableViewInteractiveBorderSize, (self.bounds.size.height - kSPUserResizableViewInteractiveBorderSize)/2, kSPUserResizableViewInteractiveBorderSize, kSPUserResizableViewInteractiveBorderSize);// (3) Create the gradient to paint the anchor points.CGFloat colors [] = {0.4, 0.8, 1.0, 1.0,0.0, 0.0, 1.0, 1.0};CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, NULL, 2);CGColorSpaceRelease(baseSpace), baseSpace = NULL;// (4) Set up the stroke for drawing the border of each of the anchor points.CGContextSetLineWidth(context, 1);CGContextSetShadow(context, CGSizeMake(0.5, 0.5), 1);CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);// (5) Fill each anchor point using the gradient, then stroke the border.CGRect allPoints[6] = { upperLeft, upperRight, lowerRight, lowerLeft, middleLeft, middleRight };for (NSInteger i = 0; i < 6; i++) {CGRect currPoint = allPoints[i];CGContextSaveGState(context);CGContextAddEllipseInRect(context, currPoint);CGContextClip(context);CGPoint startPoint = CGPointMake(CGRectGetMidX(currPoint), CGRectGetMinY(currPoint));CGPoint endPoint = CGPointMake(CGRectGetMidX(currPoint), CGRectGetMaxY(currPoint));CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);CGContextRestoreGState(context);CGContextStrokeEllipseInRect(context, CGRectInset(currPoint, 1, 1));}CGGradientRelease(gradient), gradient = NULL;CGContextRestoreGState(context);}@end@implementation SPUserResizableView@synthesize contentView, minWidth, minHeight, preventsPositionOutsideSuperview, delegate;- (void)setupDefaultAttributes {borderView = [[SPGripViewBorderView alloc] initWithFrame:CGRectInset(self.bounds, kSPUserResizableViewGlobalInset, kSPUserResizableViewGlobalInset)];[borderView setHidden:YES];[self addSubview:borderView];self.minWidth = kSPUserResizableViewDefaultMinWidth;self.minHeight = kSPUserResizableViewDefaultMinHeight;self.preventsPositionOutsideSuperview = YES;}- (id)initWithFrame:(CGRect)frame {if ((self = [super initWithFrame:frame])) {[self setupDefaultAttributes];}return self;}- (id)initWithCoder:(NSCoder *)aDecoder {if ((self = [super initWithCoder:aDecoder])) {[self setupDefaultAttributes];}return self;}- (void)setContentView:(UIView *)newContentView {[contentView removeFromSuperview];contentView = newContentView;contentView.frame = CGRectInset(self.bounds, kSPUserResizableViewGlobalInset + kSPUserResizableViewInteractiveBorderSize/2, kSPUserResizableViewGlobalInset + kSPUserResizableViewInteractiveBorderSize/2);[self addSubview:contentView];// Ensure the border view is always on top by removing it and adding it to the end of the subview list.[borderView removeFromSuperview];[self addSubview:borderView];}- (void)setFrame:(CGRect)newFrame {[super setFrame:newFrame];contentView.frame = CGRectInset(self.bounds, kSPUserResizableViewGlobalInset + kSPUserResizableViewInteractiveBorderSize/2, kSPUserResizableViewGlobalInset + kSPUserResizableViewInteractiveBorderSize/2);borderView.frame = CGRectInset(self.bounds, kSPUserResizableViewGlobalInset, kSPUserResizableViewGlobalInset);[borderView setNeedsDisplay];}static CGFloat SPDistanceBetweenTwoPoints(CGPoint point1, CGPoint point2) {CGFloat dx = point2.x - point1.x;CGFloat dy = point2.y - point1.y;return sqrt(dx*dx + dy*dy);};typedef struct CGPointSPUserResizableViewAnchorPointPair {CGPoint point;SPUserResizableViewAnchorPoint anchorPoint;} CGPointSPUserResizableViewAnchorPointPair;- (SPUserResizableViewAnchorPoint)anchorPointForTouchLocation:(CGPoint)touchPoint {// (1) Calculate the positions of each of the anchor points.CGPointSPUserResizableViewAnchorPointPair upperLeft = { CGPointMake(0.0, 0.0), SPUserResizableViewUpperLeftAnchorPoint };//CGPointSPUserResizableViewAnchorPointPair upperMiddle = { CGPointMake(self.bounds.size.width/2, 0.0), SPUserResizableViewUpperMiddleAnchorPoint };CGPointSPUserResizableViewAnchorPointPair upperRight = { CGPointMake(self.bounds.size.width, 0.0), SPUserResizableViewUpperRightAnchorPoint };CGPointSPUserResizableViewAnchorPointPair middleRight = { CGPointMake(self.bounds.size.width, self.bounds.size.height/2), SPUserResizableViewMiddleRightAnchorPoint };CGPointSPUserResizableViewAnchorPointPair lowerRight = { CGPointMake(self.bounds.size.width, self.bounds.size.height), SPUserResizableViewLowerRightAnchorPoint };//CGPointSPUserResizableViewAnchorPointPair lowerMiddle = { CGPointMake(self.bounds.size.width/2, self.bounds.size.height), SPUserResizableViewLowerMiddleAnchorPoint };CGPointSPUserResizableViewAnchorPointPair lowerLeft = { CGPointMake(0, self.bounds.size.height), SPUserResizableViewLowerLeftAnchorPoint };CGPointSPUserResizableViewAnchorPointPair middleLeft = { CGPointMake(0, self.bounds.size.height/2), SPUserResizableViewMiddleLeftAnchorPoint };CGPointSPUserResizableViewAnchorPointPair centerPoint = { CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2), SPUserResizableViewNoResizeAnchorPoint };// (2) Iterate over each of the anchor points and find the one closest to the user's touch.CGPointSPUserResizableViewAnchorPointPair allPoints[7] = { upperLeft, upperRight, lowerRight, lowerLeft, middleLeft, middleRight, centerPoint };CGFloat smallestDistance = MAXFLOAT; CGPointSPUserResizableViewAnchorPointPair closestPoint = centerPoint;for (NSInteger i = 0; i < 7; i++) {CGFloat distance = SPDistanceBetweenTwoPoints(touchPoint, allPoints[i].point);if (distance < smallestDistance) {closestPoint = allPoints[i];smallestDistance = distance;}}return closestPoint.anchorPoint;}- (BOOL)isResizing {return (anchorPoint.adjustsH || anchorPoint.adjustsW || anchorPoint.adjustsX || anchorPoint.adjustsY);}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {// Notify the delegate we've begun our editing session.if (self.delegate && [self.delegate respondsToSelector:@selector(userResizableViewDidBeginEditing:)]) {[self.delegate userResizableViewDidBeginEditing:self];}[borderView setHidden:NO];UITouch *touch = [touches anyObject];anchorPoint = [self anchorPointForTouchLocation:[touch locationInView:self]];// When resizing, all calculations are done in the superview's coordinate space.touchStart = [touch locationInView:self.superview];if (![self isResizing]) {// When translating, all calculations are done in the view's coordinate space.touchStart = [touch locationInView:self];}}- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {// Notify the delegate we've ended our editing session.if (self.delegate && [self.delegate respondsToSelector:@selector(userResizableViewDidEndEditing:)]) {[self.delegate userResizableViewDidEndEditing:self];}}- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {// Notify the delegate we've ended our editing session.if (self.delegate && [self.delegate respondsToSelector:@selector(userResizableViewDidEndEditing:)]) {[self.delegate userResizableViewDidEndEditing:self];}}- (void)showEditingHandles {[borderView setHidden:NO];}- (void)hideEditingHandles {[borderView setHidden:YES];}- (void)resizeUsingTouchLocation:(CGPoint)touchPoint {// (1) Update the touch point if we're outside the superview.if (self.preventsPositionOutsideSuperview) {CGFloat border = kSPUserResizableViewGlobalInset + kSPUserResizableViewInteractiveBorderSize/2;if (touchPoint.x < border) {touchPoint.x = border;}if (touchPoint.x > self.superview.bounds.size.width - border) {touchPoint.x = self.superview.bounds.size.width - border;}if (touchPoint.y < border) {touchPoint.y = border;}if (touchPoint.y > self.superview.bounds.size.height - border) {touchPoint.y = self.superview.bounds.size.height - border;}}// (2) Calculate the deltas using the current anchor point.CGFloat deltaW = anchorPoint.adjustsW * (touchStart.x - touchPoint.x);CGFloat deltaX = anchorPoint.adjustsX * (-1.0 * deltaW);CGFloat deltaH = anchorPoint.adjustsH * (touchPoint.y - touchStart.y);CGFloat deltaY = anchorPoint.adjustsY * (-1.0 * deltaH);// (3) Calculate the new frame.CGFloat newX = self.frame.origin.x + deltaX;CGFloat newY = self.frame.origin.y + deltaY;CGFloat newWidth = self.frame.size.width + deltaW;CGFloat newHeight = self.frame.size.height + deltaH;// (4) If the new frame is too small, cancel the changes.if (newWidth < self.minWidth) {newWidth = self.frame.size.width;newX = self.frame.origin.x;}if (newHeight < self.minHeight) {newHeight = self.frame.size.height;newY = self.frame.origin.y;}// (5) Ensure the resize won't cause the view to move offscreen.if (self.preventsPositionOutsideSuperview) {if (newX < self.superview.bounds.origin.x) {// Calculate how much to grow the width by such that the new X coordintae will align with the superview.deltaW = self.frame.origin.x - self.superview.bounds.origin.x;newWidth = self.frame.size.width + deltaW;newX = self.superview.bounds.origin.x;}if (newX + newWidth > self.superview.bounds.origin.x + self.superview.bounds.size.width) {newWidth = self.superview.bounds.size.width - newX;}if (newY < self.superview.bounds.origin.y) {// Calculate how much to grow the height by such that the new Y coordintae will align with the superview.deltaH = self.frame.origin.y - self.superview.bounds.origin.y;newHeight = self.frame.size.height + deltaH;newY = self.superview.bounds.origin.y;}if (newY + newHeight > self.superview.bounds.origin.y + self.superview.bounds.size.height) {newHeight = self.superview.bounds.size.height - newY;}}self.frame = CGRectMake(newX, newY, newWidth, newHeight);touchStart = touchPoint;}- (void)translateUsingTouchLocation:(CGPoint)touchPoint {CGPoint newCenter = CGPointMake(self.center.x + touchPoint.x - touchStart.x, self.center.y + touchPoint.y - touchStart.y);if (self.preventsPositionOutsideSuperview) {// Ensure the translation won't cause the view to move offscreen.CGFloat midPointX = CGRectGetMidX(self.bounds);if (newCenter.x > self.superview.bounds.size.width - midPointX) {newCenter.x = self.superview.bounds.size.width - midPointX;}if (newCenter.x < midPointX) {newCenter.x = midPointX;}CGFloat midPointY = CGRectGetMidY(self.bounds);if (newCenter.y > self.superview.bounds.size.height - midPointY) {newCenter.y = self.superview.bounds.size.height - midPointY;}if (newCenter.y < midPointY) {newCenter.y = midPointY;}}self.center = newCenter;}- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {if ([self isResizing]) {[self resizeUsingTouchLocation:[[touches anyObject] locationInView:self.superview]];} else {[self translateUsingTouchLocation:[[touches anyObject] locationInView:self]];}}@end
Comments
Post a Comment