versiones guia espaƱol descargar actualizar ios objective-c calayer quartz-core

ios - guia - CALayer con agujero transparente en ella



qgis manual (5)

Para crear este efecto, me pareció más fácil crear una vista completa superponiendo la pantalla, luego restando partes de la pantalla usando capas y UIBezierPaths. Para una implementación de Swift:

// Create a view filling the screen. let overlay = UIView(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height)) // Set a semi-transparent, black background. overlay.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.85) // Create the initial layer from the view bounds. let maskLayer = CAShapeLayer() maskLayer.frame = overlay.bounds maskLayer.fillColor = UIColor.blackColor().CGColor // Create the frame for the circle. let radius: CGFloat = 50.0 let rect = CGRectMake( CGRectGetMidX(overlay.frame) - radius, CGRectGetMidY(overlay.frame) - radius, 2 * radius, 2 * radius) // Create the path. let path = UIBezierPath(rect: overlay.bounds) maskLayer.fillRule = kCAFillRuleEvenOdd // Append the circle to the path so that it is subtracted. path.appendPath(UIBezierPath(ovalInRect: rect)) maskLayer.path = path.CGPath // Set the mask of the view. overlay.layer.mask = maskLayer // Add the view so it is visible. self.view.addSubview(overlay)

Probé el código anterior, y aquí está el resultado:

Agregué una biblioteca a CocoaPods que abstrae mucho del código anterior y te permite crear fácilmente superposiciones con orificios rectangulares / circulares, lo que permite al usuario interactuar con las vistas detrás de la superposición. Lo usé para crear este tutorial para una de nuestras aplicaciones:

La biblioteca se llama TAOverlayView y es de código abierto bajo Apache 2.0. ¡Espero que le sea útil!

Tengo una vista simple (lado izquierdo de la imagen) y necesito crear algún tipo de superposición (lado derecho de la imagen) a esta vista. Esta superposición debería tener algo de opacidad, por lo que la vista debajo aún es parcialmente visible. Lo más importante es que esta superposición debe tener un orificio circular en el medio, de modo que no se superponga al centro de la vista (ver la imagen de abajo).

Puedo crear fácilmente un círculo como este:

int radius = 20; //whatever CAShapeLayer *circle = [CAShapeLayer layer]; circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0,radius,radius) cornerRadius:radius].CGPath; circle.position = CGPointMake(CGRectGetMidX(view.frame)-radius, CGRectGetMidY(view.frame)-radius); circle.fillColor = [UIColor clearColor].CGColor;

Y una superposición rectangular "completa" como esta:

CAShapeLayer *shadow = [CAShapeLayer layer]; shadow.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, view.bounds.size.width, view.bounds.size.height) cornerRadius:0].CGPath; shadow.position = CGPointMake(0, 0); shadow.fillColor = [UIColor grayColor].CGColor; shadow.lineWidth = 0; shadow.opacity = 0.5; [view.layer addSublayer:shadow];

Pero no tengo idea de cómo puedo combinar estas dos capas para que creen el efecto que quiero. ¿Nadie? Intenté realmente todo ... ¡Muchas gracias por la ayuda!


Pude resolver esto con la sugerencia de Jon Steinmetz. Si a alguien le importa, aquí está la solución final:

int radius = myRect.size.width; UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.mapView.bounds.size.width, self.mapView.bounds.size.height) cornerRadius:0]; UIBezierPath *circlePath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius) cornerRadius:radius]; [path appendPath:circlePath]; [path setUsesEvenOddFillRule:YES]; CAShapeLayer *fillLayer = [CAShapeLayer layer]; fillLayer.path = path.CGPath; fillLayer.fillRule = kCAFillRuleEvenOdd; fillLayer.fillColor = [UIColor grayColor].CGColor; fillLayer.opacity = 0.5; [view.layer addSublayer:fillLayer];

para Swift 3.x:

let radius = myRect.size.width let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: self.mapView.bounds.size.width, height: self.mapView.bounds.size.height), cornerRadius: 0) let circlePath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 2 * radius, height: 2 * radius), cornerRadius: radius) path.append(circlePath) path.usesEvenOddFillRule = true let fillLayer = CAShapeLayer() fillLayer.path = path.cgPath fillLayer.fillRule = kCAFillRuleEvenOdd fillLayer.fillColor = Color.background.cgColor fillLayer.opacity = 0.5 view.layer.addSublayer(fillLayer)


Solución aceptada Swift 3.0 compatible

let radius = myRect.size.width let path = UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: self.mapView.bounds.size.width, height: self.mapView.bounds.size.height), cornerRadius: 0) let circlePath = UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: 2.0*radius, height: 2.0*radius), cornerRadius: radius) path.append(circlePath) path.usesEvenOddFillRule = true let fillLayer = CAShapeLayer() fillLayer.path = path.cgPath fillLayer.fillRule = kCAFillRuleEvenOdd fillLayer.fillColor = UIColor.gray.cgColor fillLayer.opacity = 0.5 view.layer.addSublayer(fillLayer)


Tomé un enfoque similar al de animal_chin, pero soy más visual, así que configuré la mayor parte en Interface Builder usando puntos de venta y diseño automático.

Aquí está mi solución en Swift

//shadowView is a UIView of what I want to be "solid" var outerPath = UIBezierPath(rect: shadowView.frame) //croppingView is a subview of shadowView that is laid out in interface builder using auto layout //croppingView is hidden. var circlePath = UIBezierPath(ovalInRect: croppingView.frame) outerPath.usesEvenOddFillRule = true outerPath.appendPath(circlePath) var maskLayer = CAShapeLayer() maskLayer.path = outerPath.CGPath maskLayer.fillRule = kCAFillRuleEvenOdd maskLayer.fillColor = UIColor.whiteColor().CGColor shadowView.layer.mask = maskLayer


Code Swift 2.0 compatible

A partir de la respuesta @animal_inch, codigo una pequeña utilidad-clase, espero que se aprecie:

import Foundation import UIKit import CoreGraphics /// Apply a circle mask on a target view. You can customize radius, color and opacity of the mask. class CircleMaskView { private var fillLayer = CAShapeLayer() var target: UIView? var fillColor: UIColor = UIColor.grayColor() { didSet { self.fillLayer.fillColor = self.fillColor.CGColor } } var radius: CGFloat? { didSet { self.draw() } } var opacity: Float = 0.5 { didSet { self.fillLayer.opacity = self.opacity } } /** Constructor - parameter drawIn: target view - returns: object instance */ init(drawIn: UIView) { self.target = drawIn } /** Draw a circle mask on target view */ func draw() { guard (let target = target) else { print("target is nil") return } var rad: CGFloat = 0 let size = target.frame.size if let r = self.radius { rad = r } else { rad = min(size.height, size.width) } let path = UIBezierPath(roundedRect: CGRectMake(0, 0, size.width, size.height), cornerRadius: 0.0) let circlePath = UIBezierPath(roundedRect: CGRectMake(size.width / 2.0 - rad / 2.0, 0, rad, rad), cornerRadius: rad) path.appendPath(circlePath) path.usesEvenOddFillRule = true fillLayer.path = path.CGPath fillLayer.fillRule = kCAFillRuleEvenOdd fillLayer.fillColor = self.fillColor.CGColor fillLayer.opacity = self.opacity self.target.layer.addSublayer(fillLayer) } /** Remove circle mask */ func remove() { self.fillLayer.removeFromSuperlayer() } }

Entonces, donde sea en tu código:

let circle = CircleMaskView(drawIn: <target_view>) circle.opacity = 0.7 circle.draw()