2011-01-25 5 views
1

나는 왜 이것이 작동하지 않는지를 파악하려고 오랫동안 벽을 머리에 대고 머리를 두드렸다.CGPath에 CGPath를 클리핑

기본적으로 CGPath를 사용하여 그래프 (차트)를 플로팅 한 다음이를 사용하여 그라디언트를 클리핑하려고합니다. 최종 효과는 iPhone과 함께 제공되는 주식 앱과 같아야합니다.

그라디언트와 패스는 두 개의 레이어 된 (분리되지 않은) 요소로 별도로 정밀하게 그립니다. 그러나 CGContextDrawPath를 주석 처리하면 선이나 그라디언트가 화면에 표시되지 않습니다. 이 단지 일련의 선 세그먼트을 나타내는

CGContextRef context = UIGraphicsGetCurrentContext(); 

[[UIColor whiteColor] set]; 
CGContextSetLineWidth(context, 2.0f); 
CGPoint lastDrawnPt = [[points objectAtIndex:0] CGPointValue]; 
CGPoint firstDrawnPt = lastDrawnPt; 
//create the path for the gradient 
CGMutablePathRef thePath = CGPathCreateMutable(); 
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom left 
CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); 


for (int i=0; i<(points.count-1); i++) { 
    //CGPoint pt1 = [[points objectAtIndex:i] CGPointValue]; 
    CGPoint pt2 = [[points objectAtIndex:i+1] CGPointValue]; 
    if (pt2.x > lastDrawnPt.x+2) { 
     // only draw if we've moved sunstantially to the right 

     //for the gradient 
     CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); 
     CGPathAddLineToPoint(thePath, NULL, pt2.x, pt2.y); 


     lastDrawnPt = pt2; 
    } 
} 

//finish the gradient clipping path 
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); 
CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom right 
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); 
CGPathAddLineToPoint(thePath, NULL, firstDrawnPt.x, self.bounds.size.height); // bottom right 

CGPathCloseSubpath(thePath); 

//add the gradient clipping path to the context 
CGContextSaveGState(context); 
CGContextAddPath(context, thePath); 

//draw the path 
float components[4] = {1.0, 1.0, 1.0, 1.0}; 
CGContextSetStrokeColor(context, components); 
CGContextDrawPath(context,kCGPathStroke); 

//clip the path 
CGContextClip(context); 


//Draw Gradient 
UIColor *topColor = [UIColor colorWithRed: 1.0 green:1.0 blue:1.0 alpha:1.0]; 
UIColor *bottomColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.0]; 
CGColorRef colorRef[] = { [topColor CGColor], [bottomColor CGColor] }; 
CFArrayRef colors = CFArrayCreate(NULL, (const void**)colorRef, sizeof(colorRef)/sizeof(CGColorRef), &kCFTypeArrayCallBacks); 

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colors, NULL); 
CFRelease(colorSpace); 
CFRelease(colors); 

// Draw a linear gradient from top to bottom 
CGPoint gradStartPoint = CGPointMake(50.0, self.bounds.size.height); 
CGPoint gradEndPoint = CGPointMake(50.0, 0.0); 
CGContextDrawLinearGradient(context, gradient, gradStartPoint, gradEndPoint, 0); 

CFRelease(gradient); 

// Cleanup 
CGColorSpaceRelease(colorSpace); 

CGContextRestoreGState(context); 
+0

CGContextFlush 또는 CGContextSynchronize를 시도 했습니까? 레이어링과 관련이 있다고 생각합니다. 그래디언트가 그려진 후에도 CGContextClip을 움직여보십시오. – namar0x0309

+0

아이디어를 주셔서 감사합니다. 그러나 그들은 도움이되지 않았습니다. – avance

+0

나는 내 길을 만드는 방식에 뭔가 문제가 있다고 생각하기 시작했습니다. – avance

답변

1
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom left 
CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); 


for (int i=0; i<(points.count-1); i++) { 
    //CGPoint pt1 = [[points objectAtIndex:i] CGPointValue]; 
    CGPoint pt2 = [[points objectAtIndex:i+1] CGPointValue]; 
    if (pt2.x > lastDrawnPt.x+2) { 
     // only draw if we've moved sunstantially to the right 

     //for the gradient 
     CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); 
     CGPathAddLineToPoint(thePath, NULL, pt2.x, pt2.y); 


     lastDrawnPt = pt2; 
    } 
} 

//finish the gradient clipping path 
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); 
CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom right 
CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); 
CGPathAddLineToPoint(thePath, NULL, firstDrawnPt.x, self.bounds.size.height); // bottom right 

:

여기 내의 drawRect 코드입니다. 특정 포인트 값의 경우 다음과 같이 보일 수 있습니다.

| |||| | 

단일 연속 모양을 그릴 수 없습니다. 따라서이 경로는 효과적으로 비어 있으므로 클리핑에 쓸모가 없습니다. 이전에 본 것처럼 클리핑하면 클리핑 패스 내부에 더 이상 드로잉되지 않습니다.

마다 lineto, curveto, arc 등이 현재 지점에서 작동합니다. 당신은 설정 처음 moveto 있지만, 각 lineto, curveto, arc 등이없는 명확 현재 위치, 그것은 업데이트 그것을 않습니다. 따라서 하나의 모양을 만들려면 moveto 다음에 연속 숫자 lineto (또는 curveto 또는 arc)을 쓰고 결국에는 closepath이 이어집니다.

CGPathCloseSubpath(thePath); 

이 경로의 폐쇄 된 형상을 생성하지만 ... closepath라고하면 그 형상이 제로 영역 (자체에서 다시 두배 만 선분 인)을 갖기 때문에, 그것을 클리핑 목적으로는 여전히 도움이되지 않습니다.

적어도 moveto 세그먼트 중 하나 이상을 잘라내어야한다고 생각합니다 (루프에있는 세그먼트, 그렇지 않으면 루프 세그먼트가 아닌 세그먼트). 그런 다음 인덱스를 사용하는 대신 배열에서 루프 사용 빠른 열거 형을 단순화하고 "마지막 그려지는 점"을 추적 할 수 있습니다.

또한 NSArray의 인덱스에 대한 올바른 유형은 이 아니라 NSUInteger입니다. 유형을 일치하게 유지하십시오. 나중에 도로에서 고통을 피할 수 있습니다.

+0

고마워요 피터! 그거였다. 나는 CGPath가 어떻게 작동했는지에 대해 위험 할 정도로 온건 한 이해를 가졌습니다. – avance

+0

[NSArray objectAtIndex : i]의 NSUInteger와 int에 대해서는 메서드가 자동으로 NSUInteger에 int를 캐스팅하지 않습니까? – avance

+0

@ juggleware : 아니오, 언어는 그렇지만, 처음부터 올바른 유형을 사용해서는 안된다는 의미는 아닙니다. 잘못된 타입을 사용하면, 너무 멀리까지 카운트하거나 너무 많이 카운트 할 수 없게 될 수도 있습니다 (네거티브 범위로 넘칠 경우 후자입니다. NSUInteger와 같은 unsigned 타입으로 캐스트 할 때, 거대한 숫자가 됨). 올바른 유형을 사용하면 이러한 문제를 피할 수 있습니다. –