Detecting Touch on UIImageView and Drawing Lines Between View Views While Restricting the Line

Detecting Touch on UIImageView and Drawing Lines

Introduction

In this article, we will explore how to detect touch on a UIImageView and draw lines from one point to another while restricting the line to only be drawn between two image views. We will also discuss the best practices for custom drawing on UIView subclasses.

Understanding Touch Events

When working with touches, it’s essential to understand the different events that can occur:

  • touchesBegan: This method is called when a touch is first detected by the view. It provides the starting point of the touch.
  • touchesMoved: This method is called whenever the touch moves. It provides the current location of the touch and allows you to update your drawing accordingly.
  • touchesEnded: This method is called when the touch ends. It’s an opportunity to reset your drawing state.

Enabling Touches on UIImageView

By default, UIImageView has touches disabled. To enable touches, we need to call:

- (void)setUserInteractionEnabled:(BOOL)userInteractionEnabled {
    //...
}

In our case, we’ll set it to YES.

@interface GestureView : UIView
// ...
@end

@implementation GestureView

- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        [self setUserInteractionEnabled:YES]; // enable touches
        _originOfTouchPoint = CGPointMake(0.0, 0.0);
        _currentFingerPositionPoint = CGPointMake(100.0, 100.0);
        _strokeWidth = 2.0;
    }
    return self;
}

@end

Custom Drawing

UIImageView is optimized to draw its images to the display and will not call drawRect: on its subclasses. To implement custom drawing, we should use UIView as our base class.

@interface GestureView : UIView
// ...
@end

@implementation GestureView

- (void)drawRect:(CGRect)rect {
    // create a new graphics context to draw in
    CGContextRef context = UIGraphicsGetCurrentContext();
    // set the stroke color
    CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
    // set the line width
    CGContextSetLineWidth(context, _strokeWidth);
    // move to the starting point of the line
    CGContextMoveToPoint(context, _originOfTouchPoint.x, _originOfTouchPoint.y);
    // draw a line from the current touch location to the previous one
    CGContextAddLineToPoint(context, _currentFingerPositionPoint.x, _currentFingerPositionPoint.y);
    // stroke the path
    CGContextStrokePath(context);
}

// ...
@end

Restricting Drawing Between Image Views

To restrict drawing between two image views, we need to keep track of which images are currently being touched.

@interface GestureView : UIView
// ...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    // get the starting point and view touched
    _originOfTouchPoint = [[touches anyObject] locationInView:self];
    _touchStartedObject = [[touches anyObject] view];
    
    // check if we're touching an imageView
    UIImageView *imageView = (_touchStartedObject != nil) ? (UIImageView *)_touchStartedObject : nil;
    
    if (imageView != nil) {
        // add the image view to our set of touched views
        [self touchedViews addObject:imageView];
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    // get the ending point and view ended on
    CGPoint endedPoint = [[touches anyObject] locationInView:self];
    
    // remove the view from our set of touched views
    UIImageView *imageView = [self touchedViews firstObject];
    if (imageView != nil) {
        [_touchedViews removeObject:imageView];
    }
}

- (NSArray *)touchedViews;
@end

@implementation GestureView

- (NSArray *)touchedViews {
    return _touchedViews;
}

// ...

@end

In this code, we create an array to keep track of the image views that are currently being touched. We add each view to this array when it’s first touched and remove it when it’s ended.

We can use a custom touchesBegan method on our subclass of UIView to get the starting point of the touch and determine which image views are currently being touched.

Example Use Case

Here is an example of how we might use these classes:

@interface ViewController : UIViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // create a new gesture view
    GestureView *gestureView = [[GestureView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:gestureView];
}

@end

This code creates a new ViewController and adds a GestureView to its subview. The GestureView has three image views, and the user can touch any of them.

When the user touches an image view, the GestureView will start tracking that image view. If the user then moves their finger between two different image views while still holding down on one, a line will be drawn from the first point to the second point.

If the user lifts their finger off an image view without having moved it to another one, no line will be drawn at all.

By keeping track of which images are currently being touched and only drawing lines between them, we can create a very interactive experience for our users.


Last modified on 2023-06-12