編集:今回は短いテストを実装して、答えを書き直しました。self.lineDrawView.lines
以下のソースコードは次のことを行います。タッチダウン時に、リスト内のすべてのポイントから現在のタッチポイントまでの距離をチェックします。距離が10ピクセル未満の場合、現在のタッチポイントとそのリストインデックスが保存されます。
一致するポイントが見つからない場合、別の関数が内部のペアで構成されるすべてのラインを反復処理し、self.lineDrawView.lines
各ラインまでの距離(ここで借用したラインコードまでの距離)を計算します。ここでも、距離が10ピクセル未満の場合、現在のタッチポイントと、現在のラインの開始点と終了点が保存されます。
次に、タッチ移動時に、保存されたポイントが移動されるか、保存されたラインが前のタッチダウンポイントと現在のタッチ位置の間の距離によって再計算されます。
ビューコントローラのヘッダー:
#import <UIKit/UIKit.h>
#import "myView.h"
@interface ViewController : UIViewController
{
CGPoint dragStartingPoint, lineOriginStart, lineOriginEnd;
int currentPointIndex, currentLineIndex;
}
@property (retain) myView *lineDrawView;
@property (retain) UITouch *currentTouch;
@end
ビューコントローラのソース:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.lineDrawView = [[myView alloc] initWithFrame:self.view.frame];
[self.view addSubview:self.lineDrawView];
self.lineDrawView.lines = [[NSMutableArray alloc] init];
[self.lineDrawView.lines addObject:[NSValue valueWithCGPoint:CGPointMake( 10, 10)]];
[self.lineDrawView.lines addObject:[NSValue valueWithCGPoint:CGPointMake(300, 100)]];
[self.lineDrawView.lines addObject:[NSValue valueWithCGPoint:CGPointMake(200, 400)]];
[self.lineDrawView.lines addObject:[NSValue valueWithCGPoint:CGPointMake( 50, 300)]];
}
-(float)distanceOfPoint:(CGPoint)p toLineWith:(CGPoint)v0 and:(CGPoint)v1
{
float vx = v0.x - p.x;
float vy = v0.y - p.y;
float ux = v1.x - v0.x;
float uy = v1.y - v0.y;
float length = ux * ux + uy * uy;
float result;
float det = (-vx * ux) + (-vy * uy);
// if this is < 0 or > length then it's outside the line segment
if(det < 0)
result = (v0.x - p.x) * (v0.x - p.x) + (v0.y - p.y) * (v0.y - p.y);
else if(det > length)
result = (v1.x - p.x) * (v1.x - p.x) + (v1.y - p.y) * (v1.y - p.y);
else
{
det = ux * vy - uy * vx;
result = (det * det) / length;
}
return sqrtf(result);
}
-(int)getLineNearToPoint:(CGPoint)p withMaximumDistance:(float)d
{
CGPoint p1, p2;
NSValue *v1, *v2;
for(int i=0; i<self.lineDrawView.lines.count/2; i++)
{
v1 = [self.lineDrawView.lines objectAtIndex:i*2+0];
v2 = [self.lineDrawView.lines objectAtIndex:i*2+1];
p1 = [v1 CGPointValue];
p2 = [v2 CGPointValue];
if([self distanceOfPoint:p toLineWith:p1 and:p2]<=d) return i;
}
return -1;
}
-(int)getPointNearToPoint:(CGPoint)p withinRadius:(float)r
{
float dx, dy;
CGPoint p2;
NSValue *v;
for(int i=0; i<self.lineDrawView.lines.count; i++)
{
v = [self.lineDrawView.lines objectAtIndex:i];
p2 = [v CGPointValue];
dx = p.x - p2.x;
dy = p.y - p2.y;
if(sqrtf(dx*dx + dy*dy)<=r) return i;
}
return -1;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
int iPoint, iLine;
currentLineIndex = -1;
currentPointIndex = -1;
for(UITouch *t in touches)
{
// check if a starting/ending point is near the current touch
CGPoint p = [t locationInView:self.view];
iPoint = [self getPointNearToPoint:p withinRadius:10];
if(iPoint != -1)
{
currentPointIndex = iPoint;
self.currentTouch = t;
}
// check if current touch is near a line
iLine = [self getLineNearToPoint:p withMaximumDistance:10];
if((iLine != -1) && (iPoint == -1))
{
currentLineIndex = iLine;
self.currentTouch = t;
// save current touch position
dragStartingPoint = p;
// save original starting/ending point
NSValue *v1 = [self.lineDrawView.lines objectAtIndex:iLine*2+0];
NSValue *v2 = [self.lineDrawView.lines objectAtIndex:iLine*2+1];
lineOriginStart = [v1 CGPointValue];
lineOriginEnd = [v2 CGPointValue];
}
// only use first touch, discard the rest
break;
}
[self.lineDrawView setNeedsDisplay];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *t in touches)
{
// only respond to touch move events of the touch previously assigned
// to a point or line
if(t != self.currentTouch) continue;
CGPoint p = [t locationInView:self.view];
// Are we moving a starting/ending point?
if(currentPointIndex != -1)
{
NSValue *v = [NSValue valueWithCGPoint:p];
[self.lineDrawView.lines replaceObjectAtIndex:currentPointIndex withObject:v];
}
// Are we moving a line?
if(currentLineIndex != -1)
{
// calculate drag distance
float dx = p.x - dragStartingPoint.x;
float dy = p.y - dragStartingPoint.y;
// calculate new starting/ending points
CGPoint p1 = CGPointMake(lineOriginStart.x+dx, lineOriginStart.y+dy);
CGPoint p2 = CGPointMake(lineOriginEnd.x+dx, lineOriginEnd.y+dy);
NSValue *v1 = [NSValue valueWithCGPoint:p1];
NSValue *v2 = [NSValue valueWithCGPoint:p2];
// replace old values
[self.lineDrawView.lines replaceObjectAtIndex:currentLineIndex*2+0 withObject:v1];
[self.lineDrawView.lines replaceObjectAtIndex:currentLineIndex*2+1 withObject:v2];
}
// only use first touch, discard the rest
break;
}
[self.lineDrawView setNeedsDisplay];
}
- (void)dealloc
{
self.lineDrawView.lines = nil;
[super dealloc];
}
@end
描画用のビューのヘッダー:
#import <UIKit/UIKit.h>
@interface myView : UIView
@property (retain) NSMutableArray *lines;
@end
描画のビューのソース:
#import "myView.h"
@implementation myView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self) {
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
// fill background
CGContextSetRGBFillColor(context, 0.5, 0.5, 0.5, 1.0);
CGContextFillRect(context, self.frame);
// draw lines
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextSetLineWidth(context, 2.0);
for(int i=0; i<self.lines.count/2; i++)
{
CGPoint p1 = [[self.lines objectAtIndex:i*2+0] CGPointValue];
CGPoint p2 = [[self.lines objectAtIndex:i*2+1] CGPointValue];
CGContextMoveToPoint(context, p1.x, p1.y);
CGContextAddLineToPoint(context, p2.x, p2.y);
}
CGContextStrokePath(context);
}
@end