webapp sizes pwa name medium icon for capable apple app iphone ios quartz-2d cgpath

iphone - sizes - Dibuje un brillo alrededor del borde interior de mĂșltiples CGPaths



pwa ios medium (2)

Aquí hay dos formas en Swift 3:

código de ruta:

let radius = rect.height * 0.25 let centerX = rect.width * 0.5 let centerY = rect.height * 0.5 let arcCenterOffset = radius - radius * 0.5 * sqrt(3) let degree:(_: CGFloat) -> CGFloat = { return CGFloat.pi * $0 / 180 } let gourd = UIBezierPath() let circle1 = UIBezierPath(arcCenter: CGPoint(x: centerX - radius + arcCenterOffset, y: centerY), radius: radius, startAngle: degree(-30), endAngle: degree(30), clockwise: false) let circle2 = UIBezierPath(arcCenter: CGPoint(x: centerX + radius - arcCenterOffset, y: centerY ), radius: radius, startAngle: degree(150), endAngle: degree(-150), clockwise: false) gourd.append(circle1) gourd.append(circle2) let gourdInverse = UIBezierPath(cgPath: gourd.cgPath) let infiniteRect = UIBezierPath(rect: .infinite) gourdInverse.append(infiniteRect) guard let c = UIGraphicsGetCurrentContext() else { fatalError("current context not found.") }

  1. regla de relleno incluso impar:

    c.beginPath() c.addPath(gourdInverse.cgPath) c.setShadow(offset: CGSize.zero, blur: 10, color: UIColor.red.cgColor) c.setFillColor(UIColor(white: 1, alpha: 1).cgColor) c.fillPath(using: .evenOdd)

  2. acortar

    c.beginPath() c.addPath(gourd.cgPath) c.clip() c.beginPath() c.addPath(gourdInverse.cgPath) c.setShadow(offset: CGSize.zero, blur: 10, color: UIColor.red.cgColor) c.fillPath()

imagen http://img403.imageshack.us/img403/9582/paths.jpg

Si creo un CGMutablePathRef sumando dos caminos circulares como se muestra en la imagen de la izquierda, ¿es posible obtener un CGPathRef final que represente solo el borde exterior como se muestra en la imagen de la derecha?

¡Gracias por cualquier ayuda!


Lo que estás pidiendo es la unión de los caminos de bezier. Apple no envía ninguna API para calcular la unión de rutas. De hecho, es un algoritmo bastante complicado. Aquí hay un par de enlaces:

Si explica lo que quiere hacer con la ruta de la unión, podríamos sugerirle algunas alternativas que no requieran realmente computar la unión.

Puedes dibujar un brillo interior bastante decente sin calcular realmente la unión de los caminos. En su lugar, hacer un mapa de bits. Rellena cada ruta en el mapa de bits. Usarás esto como la máscara. A continuación, cree una imagen invertida de la máscara, que tenga todo fuera del área de unión llena. Dibujará esto para hacer que CoreGraphics dibuje una sombra alrededor del borde interior de la unión. Finalmente, establezca la máscara como su máscara CGContext, establezca los parámetros de sombra y dibuje la imagen invertida.

Ok, eso suena complicado. Pero así es como se ve (versión de Retina a la derecha):

No es perfecto (demasiado claro en las esquinas), pero es bastante bueno.

Así que aquí está el código. Estoy pasando por UIBezierPaths en lugar de CGPaths, pero es trivial convertir entre ellos. Utilizo algunas funciones y objetos de UIKit. Recuerde que siempre puede hacer que UIKit dibuje a un CGContext arbitrario utilizando UIGraphicsPushContext y UIGraphicsPopContext .

Primero, necesitamos una imagen de máscara. Debe ser una imagen solo de canal alfa que es 1 dentro de cualquiera de las rutas y 0 fuera de todas las rutas. Este método devuelve tal imagen:

- (UIImage *)maskWithPaths:(NSArray *)paths bounds:(CGRect)bounds { // Get the scale for good results on Retina screens. CGFloat scale = [UIScreen mainScreen].scale; CGSize scaledSize = CGSizeMake(bounds.size.width * scale, bounds.size.height * scale); // Create the bitmap with just an alpha channel. // When created, it has value 0 at every pixel. CGContextRef gc = CGBitmapContextCreate(NULL, scaledSize.width, scaledSize.height, 8, scaledSize.width, NULL, kCGImageAlphaOnly); // Adjust the current transform matrix for the screen scale. CGContextScaleCTM(gc, scale, scale); // Adjust the CTM in case the bounds origin isn''t zero. CGContextTranslateCTM(gc, -bounds.origin.x, -bounds.origin.y); // whiteColor has all components 1, including alpha. CGContextSetFillColorWithColor(gc, [UIColor whiteColor].CGColor); // Fill each path into the mask. for (UIBezierPath *path in paths) { CGContextBeginPath(gc); CGContextAddPath(gc, path.CGPath); CGContextFillPath(gc); } // Turn the bitmap context into a UIImage. CGImageRef cgImage = CGBitmapContextCreateImage(gc); CGContextRelease(gc); UIImage *image = [UIImage imageWithCGImage:cgImage scale:scale orientation:UIImageOrientationDownMirrored]; CGImageRelease(cgImage); return image; }

Esa fue en realidad la parte difícil. Ahora necesitamos una imagen que sea nuestro color resplandeciente en cualquier lugar fuera del área de la máscara (unión de ruta). Podemos usar las funciones de UIKit para hacer esto más fácil que un enfoque CoreGraphics puro:

- (UIImage *)invertedImageWithMask:(UIImage *)mask color:(UIColor *)color { CGRect rect = { CGPointZero, mask.size }; UIGraphicsBeginImageContextWithOptions(rect.size, NO, mask.scale); { // Fill the entire image with color. [color setFill]; UIRectFill(rect); // Now erase the masked part. CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, mask.CGImage); CGContextClearRect(UIGraphicsGetCurrentContext(), rect); } UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }

Con esas dos imágenes, podemos dibujar un brillo interno en el contexto actual de los gráficos UIKit para una variedad de rutas:

- (void)drawInnerGlowWithPaths:(NSArray *)paths bounds:(CGRect)bounds color:(UIColor *)color offset:(CGSize)offset blur:(CGFloat)blur { UIImage *mask = [self maskWithPaths:paths bounds:bounds]; UIImage *invertedImage = [self invertedImageWithMask:mask color:color]; CGContextRef gc = UIGraphicsGetCurrentContext(); // Save the graphics state so I can restore the clip and // shadow attributes after drawing. CGContextSaveGState(gc); { CGContextClipToMask(gc, bounds, mask.CGImage); CGContextSetShadowWithColor(gc, offset, blur, color.CGColor); [invertedImage drawInRect:bounds]; } CGContextRestoreGState(gc); }

Para probarlo, creé una imagen usando un par de círculos y la puse en un UIImageView:

- (void)viewDidLoad { [super viewDidLoad]; UIBezierPath *path1 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(20, 20, 60, 60)]; UIBezierPath *path2 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 60, 60)]; NSArray *paths = [NSArray arrayWithObjects:path1, path2, nil]; UIGraphicsBeginImageContextWithOptions(self.imageView.bounds.size, NO, 0.0); { [self drawInnerGlowWithPaths:paths bounds:self.imageView.bounds color:[UIColor colorWithHue:0 saturation:1 brightness:.8 alpha:.8] offset:CGSizeZero blur:10.0]; } imageView.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); }