2012-10-15 43 views
0

我需要創建一個簡單的繪圖應用程序。這個應用程序包括一個兩個錨點,在每一端的一個線。用戶應該能夠觸摸錨點並拖動它來操縱線的長度和斜率。此外,用戶應該能夠觸及連接兩個錨點的線,以將其移動並將屏幕上的錨點移動。iPhone與錨點劃線

我有點難倒在這裏,我知道我可以通過簡單地使用touch down/up來操縱長度和坡度,但我不認爲這將適用於多行?!任何人都可以指出我應該看的方向嗎?非常感謝:)

回答

1

編輯:我重寫了答案,這次通過實施一個簡短的測試。下面的源代碼執行以下操作:觸摸時,它會檢查列表self.lineDrawView.lines中每個點到當前觸點的距離。如果距離低於10 px,則保存當前觸點及其列表索引。

如果找不到匹配的點,則另一個函數遍歷self.lineDrawView.lines內部由成對構成的所有線,並計算到每條線的距離(到借用的線路代碼的距離here)。再次,如果距離低於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 
+0

Hey nullp01nter,感謝您的sugg但是這並沒有考慮觸摸是否在不同線上的兩點之間的距離相同(例如,如果一行的末尾重疊/接近另一行的開頭),它也不會告訴我觸摸是否是在一條線上,我需要移動屏幕周圍的線,保持其長度和坡度完好無損。 – bennythemink

+0

我重寫了我的答案,因爲我錯過了線條運動的要點。另一個問題 - 兩條不同線路之間的距離相同 - 取決於你想在這種情況下做什麼。我上面的解決方案只需要第一個匹配點。你也可以搜索最近的點。同樣的近點是不同的,我相信你可以根據我上面的建議來實現你想要的行爲。 – nullp01nter

+0

感謝所有努力nullp01nter。我還沒有機會測試你的代碼,但邏輯看起來很合理。 – bennythemink

1

我一直在類似的問題,我結束了使用UIWebView的類似於此鏈接

http://mbostock.github.com/protovis/ex/splines.html

,我還可以使用

[myWebview stringByEvaluatingJavaScriptFromString....] 

我得到的點列表的東西必須對JS代碼進行一些更改以處理觸摸開始和觸摸結束事件,而不是鼠標向下和鼠標向上,以使其在iPad/iPhone上運行

+0

感謝馬哈茂德,但我希望在不借助Javascript的情況下在Objective C中提供解決方案。我提高了你的答案,因爲我仍然認爲它的解決方案可能與其他方面有關。 – bennythemink