2011-12-30 17 views
10

iOS 5 ha cambiato il modo in cui i percorsi built-in Google Maps App disegna:Disegno MKMapView Overlay come Google Maps Indicazioni

Apple's line

Vorrei ora di replicare il disegno della sovrapposizione percorso nella mia app ma al momento sono solo in grado di tracciare una semplice linea blu. Vorrei aggiungere l'effetto 3D con il gradiente, i bordi e il bagliore. Qualche idea su come realizzare questo?

Attualmente sto utilizzando il seguente codice:

CGContextSetFillColorWithColor(context, fillColor.CGColor); 
CGContextSetLineJoin(context, kCGLineJoinRound); 
CGContextSetLineCap(context, kCGLineCapRound); 
CGContextSetLineWidth(context, lineWidth); 
CGContextAddPath(context, path); 
CGContextReplacePathWithStrokedPath(context); 
CGContextFillPath(context); 

risultante in una linea piuttosto brutto:

my line

Grazie!

Aggiornamento: la soluzione dovrebbe funzionare su iOS 4.0 e versioni successive.

+0

Sei in grado di pubblicare il codice che stai utilizzando per tracciare le linee, al momento? –

+0

@sudo rm -rf: ho aggiunto il codice di disegno. – myell0w

risposta

7

Penso che @ChrisMiles sia corretto in quanto i segmenti vengono probabilmente disegnati individualmente. (Inizialmente pensavo che sarebbe stato possibile farlo usando CGPatternRef ma non avete alcun accesso al CTM o agli endpoint del percorso all'interno del callback del disegno.)

Con questo in mente, ecco un punto di vista estremamente esempio di come si potrebbe iniziare un tale sforzo (riempiendo i segmenti individualmente). Si noti che:

  • colori sfumati sono indovinato
  • tappi terminali sono inesistenti e dovranno essere attuate separatamente
  • alcuni artefatti di aliasing rimangono
  • non una grande attenzione è stata dedicata alle prestazioni

Speriamo che questo possa iniziare almeno (e funziona attraverso parte della geometria analitica).

- (CGGradientRef)lineGradient 
{ 
    static CGGradientRef gradient = NULL; 
    if (gradient == NULL) { 
     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
     CGColorRef white = [[UIColor colorWithWhite:1.f 
               alpha:0.7f] CGColor]; 
     CGColorRef blue = [[UIColor colorWithRed:0.1f 
              green:0.2f 
              blue:1.f 
              alpha:0.7f] CGColor]; 
     CGColorRef lightBlue = [[UIColor colorWithRed:0.4f 
               green:0.6f 
               blue:1.f 
               alpha:0.7f] CGColor]; 
     CFMutableArrayRef colors = CFArrayCreateMutable(kCFAllocatorDefault, 
                 8, 
                 NULL); 
     CFArrayAppendValue(colors, blue); 
     CFArrayAppendValue(colors, blue); 
     CFArrayAppendValue(colors, white); 
     CFArrayAppendValue(colors, white); 
     CFArrayAppendValue(colors, lightBlue); 
     CFArrayAppendValue(colors, lightBlue); 
     CFArrayAppendValue(colors, blue); 
     CFArrayAppendValue(colors, blue); 
     CGFloat locations[8] = {0.f, 0.08f, 0.14f, 0.21f, 0.29f, 0.86f, 0.93f, 1.f}; 
     gradient = CGGradientCreateWithColors(colorSpace, 
               colors, 
               locations); 
     CFRelease(colors); 
     CGColorSpaceRelease(colorSpace); 
    } 
    return gradient; 
} 

- (void)drawRect:(CGRect)rect 
{ 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGContextSaveGState(context); 

    CGContextSetAllowsAntialiasing(context, YES); 
    CGContextSetShouldAntialias(context, YES); 

    // Fill background color 
    [[UIColor whiteColor] setFill]; 
    UIRectFill(rect); 

    // Build a path 
    CGFloat strokeWidth = 10.f; 
    CGContextSetLineWidth(context, strokeWidth); 

    CGGradientRef gradient = [self lineGradient]; 

    CGPoint points[9] = { 
     CGPointMake(10.f, 25.f), 
     CGPointMake(100.f, 100.f), 
     CGPointMake(100.f, 150.f), 
     CGPointMake(22.f, 300.f), 
     CGPointMake(230.f, 400.f), 
     CGPointMake(230.f, 200.f), 
     CGPointMake(300.f, 200.f), 
     CGPointMake(310.f, 160.f), 
     CGPointMake(280.f, 100.f) 
    }; 


    for (NSUInteger i = 1; i < 9; i++) { 
     CGPoint start = points[i - 1]; 
     CGPoint end = points[i]; 
     CGFloat dy = end.y - start.y; 
     CGFloat dx = end.x - start.x; 
     CGFloat xOffset, yOffset; 
     // Remember that, unlike Cartesian geometry, origin is in *upper* left! 
     if (dx == 0) { 
      // Vertical to start, gradient is horizontal 
      xOffset = 0.5 * strokeWidth; 
      yOffset = 0.f; 
      if (dy < 0) { 
       xOffset *= -1; 
      } 
     } 
     else if (dy == 0) { 
      // Horizontal to start, gradient is vertical 
      xOffset = 0.f; 
      yOffset = 0.5 * strokeWidth; 
     } 
     else { 
      // Sloped 
      CGFloat gradientSlope = - dx/dy; 
      xOffset = 0.5 * strokeWidth/sqrt(1 + gradientSlope * gradientSlope); 
      yOffset = 0.5 * strokeWidth/sqrt(1 + 1/(gradientSlope * gradientSlope)); 
      if (dx < 0 && dy > 0) { 
       yOffset *= -1; 
      } 
      else if (dx > 0 && dy < 0) { 
       xOffset *= -1; 
      } 
      else if (dx < 0 && dy < 0) { 
       yOffset *= -1; 
       xOffset *= -1; 
      } 
      else { 
      } 
     } 
     CGAffineTransform startTransform = CGAffineTransformMakeTranslation(-xOffset, yOffset); 
     CGAffineTransform endTransform = CGAffineTransformMakeTranslation(xOffset, -yOffset); 
     CGPoint gradientStart = CGPointApplyAffineTransform(start, startTransform); 
     CGPoint gradientEnd = CGPointApplyAffineTransform(start, endTransform); 

     CGContextSaveGState(context); 
     CGContextMoveToPoint(context, start.x, start.y); 
     CGContextAddLineToPoint(context, end.x, end.y); 
     CGContextReplacePathWithStrokedPath(context); 
     CGContextClip(context); 
     CGContextDrawLinearGradient(context, 
            gradient, 
            gradientStart, 
            gradientEnd, 
            kCGGradientDrawsAfterEndLocation | kCGGradientDrawsBeforeStartLocation); 
     CGContextRestoreGState(context); 
    } 

    CGContextRestoreGState(context); 
} 
1

Direi che stanno disegnando un CGPath attorno alla linea originale, accarezzando i bordi e il gradiente che lo riempie. Le estremità sono limitate aggiungendo un semicerchio al CGPath.

Sarebbe un po 'più di lavoro che disegnare una singola linea e accarezzarla, ma dà loro il pieno controllo sullo stile del percorso di rendering.

+0

Suona bene, Chris, grazie. Il mio problema è come disegnare una sfumatura lungo un CGPath. Qualche idea o frammento di codice che potresti condividere? – myell0w

+0

Non so come saranno i risultati, ma puoi usare 'CGContextReplacePathWithStrokedPath()' per recuperare una struttura che puoi riempire con una sfumatura. Questa domanda precedente potrebbe essere utile: http://stackoverflow.com/questions/2737973/on-osx-how-do-i-gradient-fill-a-path-stroke – lxt

+0

Dopo un po 'di sperimentazione non riesco a trovare un modo per ottenere facilmente un riempimento sfumato per seguire un percorso, nel modo in cui lo stanno facendo. Potrebbero essere abbattere il percorso e disegnare un riempimento sfumato per ogni segmento separatamente. –

Problemi correlati