2013-12-17 1 views
16

В приложении я рисую изогнутый UIBezierPath класс MKOverlayPathView для отображения маршрутов полета. Это код, я использую:Нарисуйте градиент вдоль изогнутого UIBezierPath

- (UIBezierPath *)pathForOverlayForMapRect:(MKMapRect)mapRect { 

    ... bla bla bla ... 

    UIBezierPath *path = [UIBezierPath bezierPath]; 
    [path moveToPoint:s]; 
    [path addQuadCurveToPoint:e controlPoint:cp1]; 
    [path addLineToPoint:e2]; 
    [path addQuadCurveToPoint:s2 controlPoint:cp2]; 
    [path closePath]; 

    return path; 
    } 
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context{ 

    self.mapRect = mapRect; 

    CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); 
    CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0); 
    CGContextSetLineWidth(context, mapRect.size.height/700); 
    CGContextSetLineJoin(context, kCGLineJoinRound); 
    CGContextSetLineCap(context, kCGLineCapRound); 

    CGContextAddPath(context, [self pathForOverlayForMapRect:mapRect].CGPath); 

    [self updateTouchablePathForMapRect:mapRect]; 

    CGContextDrawPath(context, kCGPathFillStroke); 

} 

Это работает просто отлично, но я хотел бы обратить градиент вдоль этого пути, а не только цвет заливки. И здесь это начинает очень сложно.

Я экспериментировал с CGContextDrawLinearGradient(), но он еще нигде не полезен.

ответ

23

Хитрость заключается в том, чтобы использовать путь хода линии (CGContextReplacePathWithStrokedPath) и закрепить его (CGContextClip), чтобы ограничить градиент на пути:

// Create a gradient from white to red 
CGFloat colors [] = { 
    1.0, 1.0, 1.0, 1.0, 
    1.0, 0.0, 0.0, 1.0 
}; 

CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB(); 
CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, NULL, 2); 
CGColorSpaceRelease(baseSpace), baseSpace = NULL; 

CGContextSetLineWidth(context, mapRect.size.height/700); 
CGContextSetLineJoin(context, kCGLineJoinRound); 
CGContextSetLineCap(context, kCGLineCapRound); 

CGContextAddPath(context, [self pathForOverlayForMapRect:mapRect].CGPath); 
CGContextReplacePathWithStrokedPath(context); 
CGContextClip(context); 

[self updateTouchablePathForMapRect:mapRect]; 

// Define the start and end points for the gradient 
// This determines the direction in which the gradient is drawn 
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect)); 
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect)); 

CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0); 
CGGradientRelease(gradient), gradient = NULL; 
+0

Этот ответ прекрасен! Большое спасибо. Я удалил 'CGContextReplacePathWithStrokedPath()', поэтому мой путь был бы заполнен градиентом и не только очерчен этим. – freshking

+0

'CGContextReplacePathWithStrokedPath' полезен, если вы хотите использовать градиент _along_ путь (в соответствии с вопросом), а не _inside_ путь. – neilco

+0

Проблема, с которой я сталкивалась с 'CGContextReplacePathWithStrokedPath', заключалась в том, что градиент применялся только к самому пути, но не заполнял его. Инсульт вместо заполнения. Или у меня что-то не так? – freshking

0

В Swift 3 это может быть достигнуто с помощью ниже код , Этот пример относится к прямой линии, но должны применяться те же принципы.

let startPoint = CGPoint(x:100, y:100) 
    let endPoint = CGPoint(x: 300, y:400) 

    let context = UIGraphicsGetCurrentContext()! 
    context.setStrokeColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0); 
    // create a line 
    context.move(to: startPoint) 
    context.addLine(to: endPoint) 
    context.setLineWidth(4) 
    // use the line created above as a clipping mask 
    context.replacePathWithStrokedPath() 
    context.clip() 

    // create a gradient 
    let locations: [CGFloat] = [ 0.0, 0.5 ] 

    let colors = [UIColor.green.cgColor, 
        UIColor.white.cgColor] 

    let colorspace = CGColorSpaceCreateDeviceRGB() 

    let gradient = CGGradient(colorsSpace: colorspace, 
           colors: colors as CFArray, locations: locations) 

    let gradientStartPoint = CGPoint(x: rect.midX, y: rect.minY) 
    let gradientEndPoint = CGPoint(x: rect.midX, y: rect.maxY) 

    context.drawLinearGradient(gradient!, 
           start: gradientStartPoint, end: gradientEndPoint, 
           options: .drawsBeforeStartLocation) 
    UIGraphicsEndImageContext()