Skip to content

Instantly share code, notes, and snippets.

@iosandroiddev
Created November 3, 2012 09:34
Show Gist options
  • Save iosandroiddev/4006791 to your computer and use it in GitHub Desktop.
Save iosandroiddev/4006791 to your computer and use it in GitHub Desktop.
User Resize
//
// SPUserResizableView.m
// SPUserResizableView
//
// Created by Stephen Poletto on 12/10/11.
//
#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 of
what they actually see as the bounding box. */
#define kSPUserResizableViewGlobalInset 1.0
#define kSPUserResizableViewDefaultMinWidth 140
#define kSPUserResizableViewDefaultMinHeight 90.0
#define kSPUserResizableViewInteractiveBorderSize 50.0
static SPUserResizableViewAnchorPoint SPUserResizableViewNoResizeAnchorPoint = { 0.0, 0.0, 0.0, 0.0 };
static SPUserResizableViewAnchorPoint SPUserResizableViewUpperLeftAnchorPoint = { 0.0, 0.0, 0.0, 0.0 };
static SPUserResizableViewAnchorPoint SPUserResizableViewMiddleLeftAnchorPoint = { 0.0, 0.0, 0.0, 0.0 };
static SPUserResizableViewAnchorPoint SPUserResizableViewLowerLeftAnchorPoint = { 0.0, 0.0, 0.0, 0.0 };
static SPUserResizableViewAnchorPoint SPUserResizableViewUpperMiddleAnchorPoint = { 0.0, 0.0, 0.0, 0.0 };
static SPUserResizableViewAnchorPoint SPUserResizableViewUpperRightAnchorPoint = { 0.0, 0.0, 0.0, 0.0 };
static SPUserResizableViewAnchorPoint SPUserResizableViewMiddleRightAnchorPoint = { 0.0, 0.0, 0.0, 0.0 };
static SPUserResizableViewAnchorPoint SPUserResizableViewLowerRightAnchorPoint = { 0.0, 0.0, 1.0, -1.0 };
static SPUserResizableViewAnchorPoint SPUserResizableViewLowerMiddleAnchorPoint = { 0.0, 0.0, 0.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();
//CGRect lowerRight = CGRectMake(self.bounds.size.width - kSPUserResizableViewInteractiveBorderSize/2, self.bounds.size.height - kSPUserResizableViewInteractiveBorderSize/2, 50, kSPUserResizableViewInteractiveBorderSize/2);
UIImage *image = [UIImage imageNamed:@"btn_arwo.png"] ;
CGContextDrawImage(context, CGRectMake(self.bounds.size.width - kSPUserResizableViewInteractiveBorderSize/2-16, self.bounds.size.height - kSPUserResizableViewInteractiveBorderSize/2-16 , 30, 30), image.CGImage);
CGContextSaveGState(context);
// (1) Draw the bounding box.
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextAddRect(context, CGRectInset(self.bounds, kSPUserResizableViewInteractiveBorderSize/2, kSPUserResizableViewInteractiveBorderSize/2));
CGContextStrokePath(context);
CGFloat colors [] = {
0.0, 0.0, 0.0, 1.0,
0.0, 0.0, 0.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[1] = { lowerRight};
for (NSInteger i = 0; i < 1; 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:NO];
[borderView setBackgroundColor:[UIColor clearColor]];
[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:(UITextView *)newContentView andTag:(NSInteger)tagValue{
// 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];
[contentView removeFromSuperview];
contentView = newContentView;
contentView.frame = CGRectInset(self.bounds, kSPUserResizableViewGlobalInset + kSPUserResizableViewInteractiveBorderSize/2, kSPUserResizableViewGlobalInset + kSPUserResizableViewInteractiveBorderSize/2);
[contentView setBounces:NO];
[contentView setExclusiveTouch:YES];
contentView.userInteractionEnabled=NO;
[self addSubview:contentView];
UITapGestureRecognizer *doubletap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handledoubletap)];
//modify this number to recognizer number of tap
[doubletap setNumberOfTapsRequired:2];
[self addGestureRecognizer:doubletap];
UILongPressGestureRecognizer *oneFingerLongPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(oneFingerLongPressed:)];
oneFingerLongPressGesture.minimumPressDuration = 2.0;
oneFingerLongPressGesture.numberOfTouchesRequired = 1;
[oneFingerLongPressGesture setDelegate:self];
borderView.tag = tagValue;
[self addGestureRecognizer:oneFingerLongPressGesture];
}
-(void)handledoubletap{
contentView.userInteractionEnabled=YES;
[contentView becomeFirstResponder];
NSLog(@"double tap on view");
}
- (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[9] = { upperLeft, upperRight, lowerRight, lowerLeft, upperMiddle, lowerMiddle, middleLeft, middleRight, centerPoint };
CGFloat smallestDistance = MAXFLOAT; CGPointSPUserResizableViewAnchorPointPair closestPoint = centerPoint;
for (NSInteger i = 0; i < 9; 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.
[btnCross removeFromSuperview];
if (self.delegate && [self.delegate respondsToSelector:@selector(userResizableViewDidBeginEditing:)]) {
[self.delegate userResizableViewDidBeginEditing:self];
}
[borderView setHidden:NO];
UITouch *touch = [touches anyObject];
// CGPoint touchLocation = [touch locationInView:self.superview];
//
// if ([touch view] == contentView) {
// contentView.center = touchLocation;
//
// }
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:(NSInteger)tagValue {
[borderView setHidden:NO];
}
- (void)hideEditingHandles:(NSInteger)tagValue {
[borderView setHidden:YES];
}
- (void)resizeUsingTouchLocation:(CGPoint)touchPoint {
// (1) Update the touch point if we're outside the superview.
if(anchorPoint.adjustsH==SPUserResizableViewLowerRightAnchorPoint.adjustsH && anchorPoint.adjustsW==SPUserResizableViewLowerRightAnchorPoint.adjustsW)
{
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;
}
}
/*if(newWidth<=140)
newWidth= 140;
if(newHeight<=90)
newHeight = 90;*/
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]];
}
}
- (void)oneFingerLongPressed:(id)sender
{
[btnCross removeFromSuperview];
btnCross = [UIButton buttonWithType:UIButtonTypeCustom];
[btnCross setImage:[UIImage imageNamed:@"close.png"] forState:UIControlStateNormal];
[btnCross setFrame:CGRectMake(borderView.frame.size.width-28, borderView.frame.origin.y+5,20,20)];
btnCross.tag = borderView.tag;
[btnCross addTarget:delegate action:@selector(btnCrossClick:) forControlEvents:UIControlEventTouchUpInside];
[borderView addSubview:btnCross];
}
- (void)dealloc {
[contentView removeFromSuperview];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment