我需要创建一个简单的绘图应用程序。这个应用程序由一条带有两个锚点的线组成,两端各有一个。用户应该能够在锚点上触地并拖动它以操纵线的长度和斜率。此外,用户应该能够触摸连接两个锚点的线以移动它以及屏幕周围的锚点。
我在这里有点难过,我知道我可以通过简单地使用 touch down/up 来控制长度和坡度,但我认为这不适用于多条线?!谁能指出我应该看的方向吗?非常感谢 :)
我一直在研究类似的问题,最终我使用了类似于此链接的 UIWebView
http://mbostock.github.com/protovis/ex/splines.html
我可以使用
[myWebview stringByEvaluatingJavaScriptFromString....]
我必须对 JS 代码进行一些更改来处理触摸开始和触摸结束事件,而不是鼠标按下和鼠标抬起来让它在 iPad/iPhone 上工作
编辑:我重写了答案,这次是通过实施一个简短的测试。下面的源代码执行以下操作: 在触摸时,它检查列表中每个点self.lineDrawView.lines
到当前触摸点的距离。如果距离小于 10 px,则保存当前触摸点及其列表索引。
如果没有找到匹配点,则另一个函数遍历由内部对组成的所有行self.lineDrawView.lines
并计算到每行的距离(此处借用的到行代码的距离)。同样,如果距离低于 10 px,则保存当前触摸点以及当前行的起点和终点。
然后,在触摸移动时,要么移动保存的点,要么根据前一个触摸点和当前触摸位置之间的距离重新计算保存的线。
视图控制器的标题:
#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