Mohit Tomar
Mohit Tomar

Reputation: 5201

How to create a dynamic rectangle on uiimage in ios?

i am trying to crate a dynamic rectangle on imageview. it means rectangle should start on the touch and as long as user move the touch it should be bigger in that direction. friend suggest me, please.

Upvotes: 1

Views: 1339

Answers (3)

python
python

Reputation: 1427

You can create imageview programmatically and resize its frame as touch moves. See below link may it help you.

How can I draw dynamic rectangle on screen according to touchMove event on iOS

Upvotes: 0

mbm29414
mbm29414

Reputation: 11598

It sounds like maybe you want a selection rectangle on top of the UIImage, not just the ability to resize the image.

If that's the case, I'd recommend the following structure:

  1. Load the UIImage into a UIImageView and place that on screen.
  2. Create a UIView that is a sibling of the UIImageView
  3. Override the -drawRect: method of the UIView from #2 to simply draw a bordered outline.
  4. Detect your user's touches using -touchesBegan:/-touchesMoved:/-touchesEnded:, etc.
  5. Based on your logic and math from #4, adjust the UIView's frame in an animation block.

In my experience, animations of this nature are easier to make functional when using shocks and struts and -touchesBegan, etc, as opposed to Autolayout and UIGestureRecognizers, but YMMV.

Of course, what you're doing might require some advanced math, based on how you answer questions like the following:

  1. Can the user move the selection rectangle?
  2. Do I want the user to grab an "edge" to be able to resize it?
  3. How close does the user have to be to "grab" the edge?
  4. If not, is the user just "pushing" in a direction to make the rectangle bigger, no matter where their finger is?

The following code does the following:

  1. Loads an image into a UIImageView
  2. Allows the user to place a 100x100 selection rectangle (at first)
  3. Allows the user to move the selection rectangle by touching/dragging in interior of rectangle
  4. Allows the user to resize the select rectangle by grabbing one of 8 edge areas
  5. Updates drawing of selection rectangle to indicate "active" mode from "inactive" mode

Assumptions made by the code:

  1. User will interact with screen using 1 finger
  2. UIImageView takes up the entire view

Possible updates needed by this code:

  1. User can move selection rectangle partly offscreen
  2. This code doesn't DO anything with the selection rectangle (like, take a snapshot/image of selection rectangle contents)
#define GRAB_DISTANCE                       10
#define VIEW_PLACEMENT_ANIMATION_DURATION   0.1
#define VIEW_SIZING_ANIMATION_DURATION      0.1
typedef enum {
    kMSSLineTypeDashed,
    kMSSLineTypeSolid
} MSSLineType;
typedef enum {
    kMSSRectangleGrabZoneBottom,
    kMSSRectangleGrabZoneBottomLeft,
    kMSSRectangleGrabZoneBottomRight,
    kMSSRectangleGrabZoneLeft,
    kMSSRectangleGrabZoneNone,
    kMSSRectangleGrabZoneRight,
    kMSSRectangleGrabZoneTop,
    kMSSRectangleGrabZoneTopLeft,
    kMSSRectangleGrabZoneTopRight
} MSSRectangleGrabZone;
typedef enum {
    kMSSRectangleStatusNone,
    kMSSRectangleStatusPlacement,
    kMSSRectangleStatusResizing
} MSSRectangleStatus;
@interface MSSSelectionView : UIView
@property (assign, nonatomic)   MSSLineType  currentLineType;
@property (strong, nonatomic)   UIColor     *borderColor;
@end
@implementation MSSSelectionView
- (void)awakeFromNib {
    [super awakeFromNib];
    self.currentLineType = kMSSLineTypeSolid;
}
- (void)drawRect:(CGRect)rect {
    // Just make a border, 2 points wide, 1 point inset (so it is all contained by the view)
    CGContextRef context    = UIGraphicsGetCurrentContext();
    CGRect borderRect       = CGRectInset(rect, 1, 1);
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetLineWidth(context, 2.0f);
    switch (self.currentLineType) {
        case kMSSLineTypeDashed:
        {
            CGFloat lengths[2] = {3, 4};
            CGContextSetLineDash(context, 0.0f, lengths, 2);
        }
            break;

        case kMSSLineTypeSolid:
        {
            CGContextSetLineDash(context, 0.0f, NULL, 0);
        }
            break;

        default:
            break;
    }
    [self.borderColor setStroke];
    CGContextStrokeRect(context, borderRect);
}
@end
#import "MSSViewController.h"
@interface MSSViewController ()
@property (assign, nonatomic)           BOOL                     selectionIsVisible;
@property (assign, nonatomic)           MSSRectangleGrabZone     currentGrabZone;
@property (assign, nonatomic)           MSSRectangleStatus       currentStatus;
@property (strong, nonatomic) IBOutlet  MSSSelectionView        *selectionView;
@property (strong, nonatomic) IBOutlet  UIImageView             *imageView;
@end
@implementation MSSViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.imageView.image    = [UIImage imageNamed:@"image.jpg"];
    self.currentGrabZone    = kMSSRectangleGrabZoneNone;
    self.currentStatus      = kMSSRectangleStatusNone;
}
#pragma mark - Touch Handling
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    // Get a touch object (assuming just a 1-finger touch here)
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self.view];

    if (self.selectionIsVisible == NO) {
        // Showing the selection view for the first time

        // Update instance property
        self.selectionIsVisible = YES;

        // Place the rectangle (centered around touch)
        self.selectionView.center = location;

        // Unhide the rectangle
        self.selectionView.hidden = NO;

        // Set the status flag to placement
        self.currentStatus = kMSSRectangleStatusPlacement;

        // Change the border color to indicate that it's active
        self.selectionView.borderColor      = [UIColor lightGrayColor];
        self.selectionView.currentLineType  = kMSSLineTypeDashed;
        [self.selectionView setNeedsDisplay];

    } else {
        // Selection view already visible, so first make sure the touch was inside the selection view
        if (CGRectContainsPoint(self.selectionView.frame, location)) {
            // The touch was inside the selection view, so update the selection view's line drawing properties
            self.selectionView.borderColor      = [UIColor lightGrayColor];
            self.selectionView.currentLineType  = kMSSLineTypeDashed;
            [self.selectionView setNeedsDisplay];

            // Set status flag based on proximity to edge
            BOOL edgeGrabbed = [self location:location inGrabZoneForRect:self.selectionView.frame];
            if (edgeGrabbed == YES) {
                // The user has grabbed the edge, so allow selection view resizing
                self.currentStatus      = kMSSRectangleStatusResizing;
                self.currentGrabZone    = [self zoneGrabbedForPoint:location inRect:self.selectionView.frame];

            } else {
                // The user has touched the interior, so allow selection view movement/placement
                self.currentStatus      = kMSSRectangleStatusPlacement;
                self.currentGrabZone    = kMSSRectangleGrabZoneNone;
            }

        }

    }

}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    // Get a touch object (assuming just a 1-finger touch here)
    UITouch *touch              = [touches anyObject];
    CGPoint currentLocation     = [touch locationInView:self.view];
    CGPoint previousLocation    = [touch previousLocationInView:self.view];
    CGFloat xDelta              = currentLocation.x - previousLocation.x;
    CGFloat yDelta              = currentLocation.y - previousLocation.y;
    CGRect frame                = self.selectionView.frame;


    switch (self.currentStatus) {
        case kMSSRectangleStatusNone:
            // Do nothing
            break;

        case kMSSRectangleStatusPlacement:
        {
            // The entire selection view should be moved under the user's finger
            frame.origin.x      = frame.origin.x + xDelta;
            frame.origin.y      = frame.origin.y + yDelta;
        }
            break;

        case kMSSRectangleStatusResizing:
        {
            switch (self.currentGrabZone) {
                case kMSSRectangleGrabZoneBottom:
                {
                    // Make the view's frame taller or shorter based on yDelta
                    frame.size.height   = frame.size.height + yDelta;

                }
                    break;

                case kMSSRectangleGrabZoneBottomLeft:
                {
                    // Make the view's frame taller or shorter based on yDelta
                    // Make the view's frame wider or narrower PLUS move origin based on xDelta
                    frame.origin.x      = frame.origin.x    + xDelta;
                    frame.size.height   = frame.size.height + yDelta;
                    frame.size.width    = frame.size.width  - xDelta;

                }
                    break;

                case kMSSRectangleGrabZoneBottomRight:
                {
                    // Make the view's frame taller or shorter based on yDelta
                    // Make the view's frame wider or narrower based on xDelta
                    frame.size.height   = frame.size.height + yDelta;
                    frame.size.width    = frame.size.width  + xDelta;

                }
                    break;

                case kMSSRectangleGrabZoneLeft:
                {
                    // Make the view's frame wider or narrower PLUS move origin based on xDelta
                    frame.origin.x      = frame.origin.x    + xDelta;
                    frame.size.width    = frame.size.width  - xDelta;

                }
                    break;

                case kMSSRectangleGrabZoneNone:
                    // Do nothing
                    break;

                case kMSSRectangleGrabZoneRight:
                {
                    // Make the view's frame wider or narrower based on xDelta
                    frame.size.width    = frame.size.width  + xDelta;
                }
                    break;

                case kMSSRectangleGrabZoneTop:
                {
                    // Make the view's frame taller or shorter PLUS move origin based on yDelta
                    frame.origin.y      = frame.origin.y    + yDelta;
                    frame.size.height   = frame.size.height - yDelta;
                }
                    break;

                case kMSSRectangleGrabZoneTopLeft:
                {
                    // Make the view's frame wider or narrower PLUS move origin based on xDelta
                    // Make the view's frame taller or shorter PLUS move origin based on yDelta
                    frame.origin.x      = frame.origin.x    + xDelta;
                    frame.origin.y      = frame.origin.y    + yDelta;
                    frame.size.width    = frame.size.width  - xDelta;
                    frame.size.height   = frame.size.height - yDelta;
                }
                    break;

                case kMSSRectangleGrabZoneTopRight:
                {
                    // Make the view's frame wider or narrower based on xDelta
                    // Make the view's frame taller or shorter PLUS move origin based on yDelta
                    frame.origin.y      = frame.origin.y    + yDelta;
                    frame.size.height   = frame.size.height - yDelta;
                    frame.size.width    = frame.size.width  + xDelta;
                }
                    break;

                default:
                    break;
            }
        }
            break;

        default:
            break;
    }
    // Any frame changes made above should be animated here
    [UIView animateWithDuration:VIEW_PLACEMENT_ANIMATION_DURATION
                     animations:^{
                         self.selectionView.frame = frame;
                     }
                     completion:^(BOOL finished) {
                         [self.selectionView setNeedsDisplay];
                     }
     ];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    // Nothing much to do, just make the border black to indicate activity is done
    self.currentGrabZone                = kMSSRectangleGrabZoneNone;
    self.currentStatus                  = kMSSRectangleStatusNone;
    self.selectionView.borderColor      = [UIColor blackColor];
    self.selectionView.currentLineType  = kMSSLineTypeSolid;
    [self.selectionView setNeedsDisplay];

}
#pragma mark - Rectangle helper methods
- (BOOL)location:(CGPoint)location inGrabZoneForRect:(CGRect)rect {
    if (CGRectContainsPoint(rect, location)) {

        // The point is inside the rectangle, so determine if it's in the grab zone or the interior
        CGRect nonGrabZoneRect = CGRectInset(rect, GRAB_DISTANCE, GRAB_DISTANCE);

        if (CGRectContainsPoint(nonGrabZoneRect, location)) {
            // This point is in the interior (non-grab zone)
            return NO;

        } else {
            // This point is in the grab zone
            return YES;

        }

    } else {
        // The point is not inside the rectangle, which means they didn't grab the edge/border
        return NO;

    }

}
- (MSSRectangleGrabZone)zoneGrabbedForPoint:(CGPoint)point inRect:(CGRect)rect {
    CGRect topLeftGrabZone      = CGRectMake(rect.origin.x,                                     rect.origin.y,                                      GRAB_DISTANCE,                          GRAB_DISTANCE);
    CGRect topGrabZone          = CGRectMake(rect.origin.x + GRAB_DISTANCE,                     rect.origin.y,                                      rect.size.width - (2 * GRAB_DISTANCE),  GRAB_DISTANCE);
    CGRect topRightGrabZone     = CGRectMake(rect.origin.x + rect.size.width - GRAB_DISTANCE,   rect.origin.y,                                      GRAB_DISTANCE,                          GRAB_DISTANCE);

    CGRect leftGrabZone         = CGRectMake(rect.origin.x,                                     rect.origin.y + GRAB_DISTANCE,                      GRAB_DISTANCE,                          rect.size.height - (2 * GRAB_DISTANCE));

    CGRect rightGrabZone        = CGRectMake(rect.origin.x + rect.size.width - GRAB_DISTANCE,   rect.origin.y + GRAB_DISTANCE,                      GRAB_DISTANCE,                          rect.size.height - (2 * GRAB_DISTANCE));

    CGRect bottomLeftGrabZone   = CGRectMake(rect.origin.x,                                     rect.origin.y + rect.size.height - GRAB_DISTANCE,   GRAB_DISTANCE,                          GRAB_DISTANCE);
    CGRect bottomGrabZone       = CGRectMake(rect.origin.x + GRAB_DISTANCE,                     rect.origin.y + rect.size.height - GRAB_DISTANCE,   rect.size.width - (2 * GRAB_DISTANCE),  GRAB_DISTANCE);
    CGRect bottomRightGrabZone  = CGRectMake(rect.origin.x + rect.size.width - GRAB_DISTANCE,   rect.origin.y + rect.size.height - GRAB_DISTANCE,   GRAB_DISTANCE,                          GRAB_DISTANCE);

    if (CGRectContainsPoint(topLeftGrabZone, point)) {
        return kMSSRectangleGrabZoneTopLeft;

    } else if (CGRectContainsPoint(topGrabZone, point)) {
        return kMSSRectangleGrabZoneTop;

    } else if (CGRectContainsPoint(topRightGrabZone, point)) {
        return kMSSRectangleGrabZoneTopRight;

    } else if (CGRectContainsPoint(leftGrabZone, point)) {
        return kMSSRectangleGrabZoneLeft;

    } else if (CGRectContainsPoint(rightGrabZone, point)) {
        return kMSSRectangleGrabZoneRight;

    } else if (CGRectContainsPoint(bottomLeftGrabZone, point)) {
        return kMSSRectangleGrabZoneBottomLeft;

    } else if (CGRectContainsPoint(bottomGrabZone, point)) {
        return kMSSRectangleGrabZoneBottom;

    } else if (CGRectContainsPoint(bottomRightGrabZone, point)) {
        return kMSSRectangleGrabZoneBottomRight;

    } else {
        return kMSSRectangleGrabZoneNone;

    }
}
@end

Upvotes: 1

Antonio MG
Antonio MG

Reputation: 20410

You need to use Core Graphics for that, try this tutorial:

Quartz 2D Programming Guide

Upvotes: 1

Related Questions