2017-02-08 67 views
0

我想在UIImageView,上繪製一個「消失的筆畫」,它會在觸摸事件之後進行並在固定時間延遲後自行擦除。這是我在ViewController中的。使用CGContextRef繪製自我擦除路徑

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 
    UITouch *touch = [touches anyObject]; 
    CGPoint currentPoint = [touch locationInView:self.view]; 
    CGPoint lp = lastPoint; 

    UIColor *color = [UIColor blackColor]; 
    [self drawLine:5 from:lastPoint to:currentPoint color:color blend:kCGBlendModeNormal]; 

    double delayInSeconds = 1.0; 
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
     [self drawLine:brush from:lp to:currentPoint color:[UIColor clearColor] blend:kCGBlendModeClear]; 
    }); 

    lastPoint = currentPoint; 
} 

- (void)drawLine:(CGFloat)width from:(CGPoint)from to:(CGPoint)to color:(UIColor*)color blend:(CGBlendMode)mode { 

    UIGraphicsBeginImageContext(self.view.frame.size); 
    CGContextRef context = UIGraphicsGetCurrentContext(); 

    [self.tempDrawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; 

    CGContextMoveToPoint(context, from.x, from.y); 
    CGContextAddLineToPoint(context, to.x, to.y); 
    CGContextSetLineCap(context, kCGLineCapRound); 
    CGContextSetLineWidth(context, width); 
    CGContextSetStrokeColorWithColor(context, [color CGColor]); 
    CGContextSetBlendMode(context, mode); 
    CGContextStrokePath(context); 

    self.tempDrawImage.image = UIGraphicsGetImageFromCurrentImageContext(); 
    [self.tempDrawImage setAlpha:1]; 
    UIGraphicsEndImageContext(); 
} 

繪圖階段很好地工作,但後續擦除階段有幾個問題。

  1. 正確清除「填充」行時,路徑周圍會留下細小的筆劃。
  2. 「擦除階段」是波濤洶涌,遠不及繪圖階段那樣平滑。我最好的猜測是,這是由於UIGraphicsBeginImageContext運行在dispatch_after的成本。

有沒有更好的方法來繪製自擦除線?

獎勵:我真的很喜歡「縮小和消失」的道路。換句話說,在延遲之後,我不希望清除描邊路徑,而是希望將其從5pt縮小到0pt,同時淡化不透明度。

+0

創建圖形上下文,繪製圖形並捕獲圖像是實現此目的的一種非常昂貴的方式。我建議改爲使用UIView的自定義子類並重寫'drawRect'。然後,您可以直接在當前圖形上下文中執行繪圖命令。您可以在視圖上發出setNeedsDisplay以觸發視圖進行更新。您可能需要稍微增加筆劃寬度(嘗試大一點),而不是繪製階段。 –

+0

@DuncanC謝謝你。是的,我剛剛意識到越來越多的筆畫寬度可以解決鬼紋問題。我會嘗試一下你的drawInRect建議。乾杯! – CaptainStiggz

+0

擦除策略並不是一個好主意......如果您繪製一個新的線段穿越舊線路,並且舊線路被擦除,那麼新線路也會在交叉點處被擦除。 – spinalwrap

回答

0

這裏有很多很棒的答案。我認爲最理想的解決方案是使用OpenGL,因爲它將不可避免地成爲性能最高,並且在精靈,路徑和其他有趣的視覺效果方面提供最大的靈活性。

我的應用程序是一個遙控器的種類,旨在提供一個簡單的視覺幫助來跟蹤運動,而不是留下持久或高保真的筆畫。因此,我最終創建了一個簡單的子類UIView,它使用CoreGraphics繪製一個UIBezierPath。我最終將用OpenGL解決方案替換這個快速修復解決方案。

我使用的實現方式遠非完美,因爲它留下了干擾未來筆觸的白色路徑,直到用戶提起觸摸,這會重置畫布。我發佈了我使用的解決方案here,以防有人發現它有幫助。

1

我只是讓視圖以60赫茲連續繪製,並且每次使用存儲在數組中的點繪製整條線。這樣,如果您從陣列中刪除最老的點,它們將不再被繪製。

掛鉤視圖中顯示刷新頻率(60赫茲),試試這個:

displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)]; 
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 

商店年齡屬性與每個點一起,然後就在陣列上循環,去除年齡超過點你的門檻。

例如

@interface AgingPoint <NSObject> 
@property CGPoint point; 
@property NSTimeInterval birthdate; 
@end 

// ..... later, in the draw call 

NSTimeInterval now = CACurrentMediaTime(); 

AgingPoint *p = [AgingPoint new]; 
p.point = touchlocation; // get yr touch 
p.birthdate = now; 

// remove old points 
while(myPoints.count && now - [myPoints[0] birthdate] > 1) 
{ 
    [myPoints removeObjectAtIndex: 0]; 
} 
myPoints.add(p); 

if(myPoints.count < 2) 
    return; 

UIBezierPath *path = [UIBezierPath path]; 
[path moveToPoint: [myPoints[0] point]]; 
for (int i = 1; i < myPoints.count; i++) 
{ 
    [path lineToPoint: [myPoints[i] point]; 
} 

[path stroke]; 

等各個繪製調用,使一個新的bezierpath,移動到第一個點,再加入行所有其他點。最後,撫摸這條線。

要實現「收縮」行,可以在數組中的連續點對之間繪製短線,並使用age屬性計算筆畫寬度。這並不完美,因爲各個細分市場在起點和終點都會有相同的寬度,但這只是一個開始。

重要提示:如果您打算提取很多分數,性能將成爲問題。這種使用Quartz進行渲染的路徑並沒有被精確地調整以實現快速渲染。事實上,它非常非常緩慢。可可數組和對象也不是很快。

如果遇到性能問題並且想要繼續此項目,請查看OpenGL渲染。通過將簡單的C結構插入到GPU中,您將可以更快地運行它。