Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS line measurement on picture

I need some help to get started on drawing lines with circle at ends, and measure its length. I am able to draw the line but can't make it moving,having spent hours decided to post on SO.

So please see below image and guide me to get started. Any sample or tutorial using objective c will help.

enter image description here

Thanks :)

like image 853
Dave Avatar asked Jun 26 '15 06:06

Dave


People also ask

Can you get measurements from a picture on iPhone?

The Measure app uses augmented reality (AR) technology to turn your device into a tape measure. You can gauge the size of objects, automatically detect the dimensions of rectangular objects, and save a photo of the measurement.

How do I use my iPhone camera as a ruler?

Hold your phone so the camera is pointing at the object you want to measure and move the phone around until you see a white circle with a dot in the middle. Line the white dot up with a corner on the item you want to measure then press the white button with the + sign in the middle of it.


1 Answers

This idea looked like fun to implement, so I started a new project in Xcode and created a proof-of-concept.

LineView (UIView subclass)

This class is responsible for drawing two circles and a line connecting their centers.

class LineView: UIView {

    var startPoint: CGPoint?
    var endPoint: CGPoint?

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setup()
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setup()
    }

    private func setup() {
        self.backgroundColor = UIColor.clearColor()
        self.multipleTouchEnabled = true
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.updatePointsWithTouches(touches)
    }

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.updatePointsWithTouches(touches)
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.clearPoints()
    }

    override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
        self.clearPoints()
    }

    private func updatePointsWithTouches(touches: Set<UITouch>) {
        if touches.count >= 1 {
            self.startPoint = touches[advance(touches.startIndex, 0)].locationInView(self)
        }

        if touches.count >= 2 {
            self.endPoint = touches[advance(touches.startIndex, 1)].locationInView(self)
        }

        self.setNeedsDisplay()
    }

    private func clearPoints() {
        self.startPoint = nil
        self.endPoint = nil
        self.setNeedsDisplay()
    }



    // MARK: - Drawing

    override func drawRect(rect: CGRect) {
        // draw circle at startPoint
        if let sp = self.startPoint {
            self.drawTouchCircleAtPoint(sp)
        }

        // draw circle at endPoint
        if let ep = self.endPoint {
            self.drawTouchCircleAtPoint(ep)
        }

        // draw line between points
        if let sp = self.startPoint, ep = self.endPoint {
            self.drawLineBetweenFirstPoint(sp, secondPoint: ep)
        }
    }

    private func drawTouchCircleAtPoint(p: CGPoint) {
        let context = UIGraphicsGetCurrentContext()
        CGContextSaveGState(context)

        CGContextSetLineWidth(context, 2.0)
        CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.6)

        CGContextAddArc(context, p.x, p.y, CGFloat(30.0), CGFloat(0.0), CGFloat(M_PI * 2), 1)
        CGContextFillPath(context)

        CGContextRestoreGState(context)
    }

    private func drawLineBetweenFirstPoint(p1: CGPoint, secondPoint p2: CGPoint) {
        let context = UIGraphicsGetCurrentContext()
        CGContextSaveGState(context)

        CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().colorWithAlphaComponent(0.6).CGColor)
        CGContextSetLineWidth(context, 1.0)

        CGContextMoveToPoint(context, p1.x, p1.y)
        CGContextAddLineToPoint(context, p2.x, p2.y)
        CGContextStrokePath(context)

        CGContextRestoreGState(context)
    }
}

This class introduces two private properties: startPoint and endPoint, which keep track of where the user's fingers are touching the view.

In this class you'll find a setup() function that is called from all the initializers. self.multipleTouchEnabled = true is crucial here so the view can detect multiple touches at the same time.

The touchesBegan/Moved/Ended/Cancelled functions call helper functions that extract the locations of the UITouch instances in the touches set.

Finally, the last three functions are responsible for drawing the circles and the connecting line.

InteractiveImageView (UIImageView subclass)

class InteractiveImageView: UIImageView {

    private var lineView: LineView!

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setup()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setup()
    }

    private func setup() {
        self.userInteractionEnabled = true
        self.lineView = LineView(frame: self.bounds)
        self.addSubview(self.lineView)
    }

}

This UIImageView subclass has an embedded LineView to capture the multi-touch events.


You can use these classes with a Storyboard too! Just drag out a UIImageView, change it's class to InteractiveImageView, set the proper constraints, and run the app. I'll leave it up to you to draw the text on the axis between the center of the circles.

Here's a picture of my proof-of-concept.


For those looking for an Objective-C solution, please see below for the implementation files of LineView and InteractiveImageView.

LineView (in Objective-C)

#import "LineView.h"

@interface LineView ()

@property (nonatomic) CGPoint startPoint;
@property (nonatomic) CGPoint endPoint;

@end

@implementation LineView

- (instancetype)init
{
    self = [super init];
    if (self) {
        [self setup];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self setup];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

- (void)setup
{
    self.backgroundColor = [UIColor clearColor];
    self.multipleTouchEnabled = true;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self updatePointsWithTouches:touches];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self updatePointsWithTouches:touches];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self clearPoints];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self clearPoints];
}

- (void)clearPoints
{
    self.startPoint = CGPointZero;
    self.endPoint = CGPointZero;
    [self setNeedsDisplay];
}

- (void)updatePointsWithTouches:(NSSet *)touches
{
    if (touches.count >= 1) {
        UITouch *touch = [touches allObjects][0];
        self.startPoint = [touch locationInView:self];
    }

    if (touches.count >= 2) {
        UITouch *touch = [touches allObjects][1];
        self.endPoint = [touch locationInView:self];
    }

    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect
{
    if (self.startPoint.x != 0 && self.startPoint.y != 0) {
        [self drawTouchCircleAtPoint:self.startPoint];
    }

    if (self.endPoint.x != 0 && self.endPoint.y != 0) {
        [self drawTouchCircleAtPoint:self.endPoint];
    }

    if (self.endPoint.x != 0 && self.endPoint.y != 0 &&
        self.startPoint.x != 0 && self.startPoint.y != 0) {
        [self drawLineBetweenFirstPoint:self.startPoint end:self.endPoint];
    }
}

- (void)drawLineBetweenFirstPoint:(CGPoint)startPoint end:(CGPoint)endPoint
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);
    CGContextSetStrokeColorWithColor(context, [[[UIColor whiteColor] colorWithAlphaComponent:0.6] CGColor]);
    CGContextSetLineWidth(context, 1.0);

    CGContextMoveToPoint(context, startPoint.x, startPoint.y);
    CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
    CGContextStrokePath(context);
    CGContextRestoreGState(context);
}

- (void)drawTouchCircleAtPoint:(CGPoint)CirclePoint
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);
    CGContextSetLineWidth(context, 2.0);
    CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.6);

    CGContextAddArc(context, CirclePoint.x, CirclePoint.y, 30.0, 30.0,  M_PI * 2, YES);
    CGContextFillPath(context);
    CGContextRestoreGState(context);
}

@end

InteractiveImageView (in Objective-C)

#import "InteractiveImageView.h"
#import "LineView.h"

@interface InteractiveImageView ()

@property (strong, nonatomic) LineView *lineView;

@end

@implementation InteractiveImageView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self setup];
    }
    return self;
}

- (void)setup
{
    self.userInteractionEnabled = YES;
    self.lineView = [[LineView alloc] initWithFrame:self.bounds];
    [self addSubview:self.lineView];
}

@end
like image 118
ndmeiri Avatar answered Oct 12 '22 23:10

ndmeiri