ios objective-c uiview cgcontext

ios - Corte el agujero transparente en UIView



objective-c cgcontext (15)

Buscando crear una vista que tenga un marco transparente dentro de ella para que las vistas detrás de la vista se puedan ver a través de este marco transparente, pero las áreas fuera de esta no se mostrarán. Entonces esencialmente una ventana dentro de la vista.

Esperando poder hacer algo como esto:

CGRect hole = CGRectMake(100, 100, 250, 250); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor); CGContextFillRect(context, rect); CGContextAddRect(context, hole); CGContextClip(context); CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor); CGContextFillRect(context, rect);

pero el claro no anula el negro, por lo que el fondo completo es negro. ¿Alguna idea a lo largo de estas líneas?


¡Hazlo al revés! Coloque las vistas que le gustaría ver a través del "agujero" en una vista separada del tamaño correcto. Luego ajuste "clipsToBounds" a YES y ponga esa vista en la parte superior. La vista con el marco "transparente" es la más inferior a continuación. "clipsToBounds" significa que todo lo que está fuera de la caja / agujero está cortado.

Entonces es posible que tenga que tratar cómo se manejan los toques. Pero esa es otra pregunta. Quizás sea suficiente con configurar userInteractionEnabled en las vistas correspondientes.


Aquí está mi implementación general rápida.

  • Para vistas estáticas, agregue tuplas a la matriz holeViews como (theView, isRound)
  • Si desea asignar dinámicamente las vistas como necesito, configure el generador en algo, digamos quizás {someViewArray.map{($0,false)}} // array of views, not round
  • Utilice el radio de la esquina de la vista en lugar de la bandera isRound si lo desea, isRound es más fácil para hacer círculos.
  • Tenga en cuenta que isRound is isEllipseThatWillBeRoundIfTheViewIsSquare
  • La mayoría del código no necesitará los públicos / internos.

Espero que ayude a alguien, gracias a los otros colaboradores

public class HolyView : UIView { public var holeViews = [(UIView,Bool)]() public var holeViewsGenerator:(()->[(UIView,Bool)])? internal var _backgroundColor : UIColor? public override var backgroundColor : UIColor? { get {return _backgroundColor} set {_backgroundColor = newValue} } public override func drawRect(rect: CGRect) { if (backgroundColor == nil) {return} let ctxt = UIGraphicsGetCurrentContext() backgroundColor?.setFill() UIRectFill(rect) UIColor.whiteColor().setFill() UIRectClip(rect) let views = (holeViewsGenerator == nil ? holeViews : holeViewsGenerator!()) for (view,isRound) in views { let r = convertRect(view.bounds, fromView: view) if (CGRectIntersectsRect(rect, r)) { let radius = view.layer.cornerRadius if (isRound || radius > 0) { CGContextSetBlendMode(ctxt, kCGBlendModeDestinationOut); UIBezierPath(roundedRect: r, byRoundingCorners: .AllCorners, cornerRadii: (isRound ? CGSizeMake(r.size.width/2, r.size.height/2) : CGSizeMake(radius,radius)) ).fillWithBlendMode(kCGBlendModeDestinationOut, alpha: 1) } else { UIRectFillUsingBlendMode(r, kCGBlendModeDestinationOut) } } } } }


Bueno, tendré que responder como me perdí el comentario y rellené un formulario de respuesta :) Realmente me gustaría que Carsten brinde más información sobre la mejor manera de hacer lo que él propone.

Podrías usar

+ (UIColor *)colorWithPatternImage:(UIImage *)image

para crear una imagen de "color" de fondo de cualquier complejidad. Una imagen puede crearse mediante programación si está familiarizado con las clases de dibujo o estáticamente si los marcos de Windows están predefinidos.


Esta es mi implementación (ya que necesitaba una vista con partes transparentes):

Archivo de encabezado (.h):

// Subclasses UIview to draw transparent rects inside the view #import <UIKit/UIKit.h> @interface PartialTransparentView : UIView { NSArray *rectsArray; UIColor *backgroundColor; } - (id)initWithFrame:(CGRect)frame backgroundColor:(UIColor*)color andTransparentRects:(NSArray*)rects; @end

Archivo de implementación (.m):

#import "PartialTransparentView.h" #import <QuartzCore/QuartzCore.h> @implementation PartialTransparentView - (id)initWithFrame:(CGRect)frame backgroundColor:(UIColor*)color andTransparentRects:(NSArray*)rects { backgroundColor = color; rectsArray = rects; self = [super initWithFrame:frame]; if (self) { // Initialization code self.opaque = NO; } return self; } // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code [backgroundColor setFill]; UIRectFill(rect); // clear the background in the given rectangles for (NSValue *holeRectValue in rectsArray) { CGRect holeRect = [holeRectValue CGRectValue]; CGRect holeRectIntersection = CGRectIntersection( holeRect, rect ); [[UIColor clearColor] setFill]; UIRectFill(holeRectIntersection); } } @end

Ahora, para agregar una vista con transparencia parcial, debe importar la subclase UIView personalizada de PartialTransparentView y luego usarla de la siguiente manera:

NSArray *transparentRects = [[NSArray alloc] initWithObjects:[NSValue valueWithCGRect:CGRectMake(0, 50, 100, 20)],[NSValue valueWithCGRect:CGRectMake(0, 150, 10, 20)], nil]; PartialTransparentView *transparentView = [[PartialTransparentView alloc] initWithFrame:CGRectMake(0,0,200,400) backgroundColor:[UIColor colorWithWhite:1 alpha:0.75] andTransparentRects:rects]; [self.view addSubview:backgroundView];

Esto creará una vista con 2 rects transparentes. Por supuesto, puede agregar tantas repeticiones como desee o simplemente usar una. El código anterior solo está manejando rectángulos, por lo que si deseas usar círculos, tendrás que modificarlo.


Esta implementación admite rectángulos y círculos, escritos de forma rápida: PartialTransparentMaskView

class PartialTransparentMaskView: UIView{ var transparentRects: Array<CGRect>? var transparentCircles: Array<CGRect>? weak var targetView: UIView? init(frame: CGRect, backgroundColor: UIColor?, transparentRects: Array<CGRect>?, transparentCircles: Array<CGRect>?, targetView: UIView?) { super.init(frame: frame) if((backgroundColor) != nil){ self.backgroundColor = backgroundColor } self.transparentRects = transparentRects self.transparentCircles = transparentCircles self.targetView = targetView self.opaque = false } required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func drawRect(rect: CGRect) { backgroundColor?.setFill() UIRectFill(rect) // clear the background in the given rectangles if let rects = transparentRects { for aRect in rects { var holeRectIntersection = CGRectIntersection( aRect, rect ) UIColor.clearColor().setFill(); UIRectFill(holeRectIntersection); } } if let circles = transparentCircles { for aRect in circles { var holeRectIntersection = aRect let context = UIGraphicsGetCurrentContext(); if( CGRectIntersectsRect( holeRectIntersection, rect ) ) { CGContextAddEllipseInRect(context, holeRectIntersection); CGContextClip(context); CGContextClearRect(context, holeRectIntersection); CGContextSetFillColorWithColor( context, UIColor.clearColor().CGColor) CGContextFillRect( context, holeRectIntersection); } } } } }


Esto hará el recorte:

CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor( context, [UIColor blueColor].CGColor ); CGContextFillRect( context, rect ); CGRect holeRectIntersection = CGRectIntersection( CGRectMake(50, 50, 50, 50), rect ); if( CGRectIntersectsRect( holeRectIntersection, rect ) ) { CGContextAddEllipseInRect(context, holeRectIntersection); CGContextClip(context); CGContextClearRect(context, holeRectIntersection); CGContextSetFillColorWithColor( context, [UIColor clearColor].CGColor ); CGContextFillRect( context, holeRectIntersection); }


Implementación de la answer en Swift 4:

import UIKit class PartialTransparentView: UIView { var rectsArray: [CGRect]? convenience init(rectsArray: [CGRect]) { self.init() self.rectsArray = rectsArray backgroundColor = UIColor.black.withAlphaComponent(0.6) isOpaque = false } override func draw(_ rect: CGRect) { backgroundColor?.setFill() UIRectFill(rect) guard let rectsArray = rectsArray else { return } for holeRect in rectsArray { let holeRectIntersection = rect.intersection(holeRect) UIColor.clear.setFill() UIRectFill(holeRectIntersection) } } }


Incluyendo una respuesta para Xamarin Studio iOS usando C #. Dibuja un único rectángulo redondeado con un 60% de alfa. Mayormente tomado de la respuesta de @mikeho

public override void Draw(CGRect rect) { base.Draw(rect); //Allows us to draw a nice clear rounded rect cutout CGContext context = UIGraphics.GetCurrentContext(); // Create a path around the entire view UIBezierPath clipPath = UIBezierPath.FromRect(rect); // Add the transparent window to a sample rectangle CGRect sampleRect = new CGRect(0f, 0f, rect.Width * 0.5f, rect.Height * 0.5f); UIBezierPath path = UIBezierPath.FromRoundedRect(sampleRect, sampleRect.Height * 0.25f); clipPath.AppendPath(path); // This sets the algorithm used to determine what gets filled and what doesn''t clipPath.UsesEvenOddFillRule = true; context.SetFillColor(UIColor.Black.CGColor); context.SetAlpha(0.6f); clipPath.Fill(); }


La respuesta de @ mosib fue de gran ayuda para mí hasta que quise dibujar más de un corte circular en mi opinión. Después de luchar un poco, actualicé mi drawRect como este (código en swift ... lo siento mal edición):

override func drawRect(rect: CGRect) { backgroundColor.setFill() UIRectFill(rect) let layer = CAShapeLayer() let path = CGPathCreateMutable() for aRect in self.rects { let holeEnclosingRect = aRect CGPathAddEllipseInRect(path, nil, holeEnclosingRect) // use CGPathAddRect() for rectangular hole /* // Draws only one circular hole let holeRectIntersection = CGRectIntersection(holeRect, rect) let context = UIGraphicsGetCurrentContext() if( CGRectIntersectsRect(holeRectIntersection, rect)) { CGContextBeginPath(context); CGContextAddEllipseInRect(context, holeRectIntersection) //CGContextDrawPath(context, kCGPathFillStroke) CGContextClip(context) //CGContextClearRect(context, holeRectIntersection) CGContextSetFillColorWithColor(context, UIColor.clearColor().CGColor) CGContextFillRect(context, holeRectIntersection) CGContextClearRect(context, holeRectIntersection) }*/ } CGPathAddRect(path, nil, self.bounds) layer.path = path layer.fillRule = kCAFillRuleEvenOdd self.layer.mask = layer }


La respuesta de Lefteris es absolutamente correcta, sin embargo, crea Rectos transparentes. Para la capa transparente CIRCULAR, modifique dibujar rect como

- (void)drawRect:(CGRect)rect { [backgroundColor setFill]; UIRectFill(rect); for (NSValue *holeRectValue in rectsArray) { CGRect holeRect = [holeRectValue CGRectValue]; CGRect holeRectIntersection = CGRectIntersection( holeRect, rect ); CGContextRef context = UIGraphicsGetCurrentContext(); if( CGRectIntersectsRect( holeRectIntersection, rect ) ) { CGContextAddEllipseInRect(context, holeRectIntersection); CGContextClip(context); CGContextClearRect(context, holeRectIntersection); CGContextSetFillColorWithColor( context, [UIColor clearColor].CGColor ); CGContextFillRect( context, holeRectIntersection); } } }


Otra solución: el rect grande es toda la vista (color amarillo) y el pequeño es el rect transparente. La opacidad del color es configurable.

let pathBigRect = UIBezierPath(rect: bigRect) let pathSmallRect = UIBezierPath(rect: smallRect) pathBigRect.appendPath(pathSmallRect) pathBigRect.usesEvenOddFillRule = true let fillLayer = CAShapeLayer() fillLayer.path = pathBigRect.CGPath fillLayer.fillRule = kCAFillRuleEvenOdd fillLayer.fillColor = UIColor.yellowColor().CGColor //fillLayer.opacity = 0.4 view.layer.addSublayer(fillLayer)


Si quieres algo rápido y eficaz, agregué una biblioteca ( TAOverlayView ) a CocoaPods que te permite crear 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:

Puede cambiar el fondo configurando backgroundColor de la superposición con algo como UIColor(red: 0, green: 0, blue: 0, alpha: 0.85) , dependiendo de su color y necesidades de opacidad.


Terminó "Fingiendo"

windowFrame es una propiedad

CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor); CGContextFillRect(context, rect); CGRect rootFrame = [[Navigation rootController] view].frame; CGSize deviceSize = CGSizeMake(rootFrame.size.width, rootFrame.size.height); CGRect topRect = CGRectMake(0, 0, deviceSize.width, windowFrame.origin.y); CGRect leftRect = CGRectMake(0, topRect.size.height, windowFrame.origin.x, windowFrame.size.height); CGRect rightRect = CGRectMake(windowFrame.size.width+windowFrame.origin.x, topRect.size.height, deviceSize.width-windowFrame.size.width+windowFrame.origin.x, windowFrame.size.height); CGRect bottomRect = CGRectMake(0, windowFrame.origin.y+windowFrame.size.height, deviceSize.width, deviceSize.height-windowFrame.origin.y+windowFrame.size.height); CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor); CGContextFillRect(context, topRect); CGContextFillRect(context, leftRect); CGContextFillRect(context, rightRect); CGContextFillRect(context, bottomRect);


en este código crea más que el círculo

- (void)drawRect:(CGRect)rect { // Drawing code UIColor *bgcolor=[UIColor colorWithRed:0.85 green:0.85 blue:0.85 alpha:1.0f];//Grey [bgcolor setFill]; UIRectFill(rect); if(!self.initialLoad){//If the view has been loaded from next time we will try to clear area where required.. // clear the background in the given rectangles for (NSValue *holeRectValue in _rectArray) { CGContextRef context = UIGraphicsGetCurrentContext(); CGRect holeRect = [holeRectValue CGRectValue]; [[UIColor clearColor] setFill]; CGRect holeRectIntersection = CGRectIntersection( holeRect, rect ); CGContextSetFillColorWithColor( context, [UIColor clearColor].CGColor ); CGContextSetBlendMode(context, kCGBlendModeClear); CGContextFillEllipseInRect( context, holeRectIntersection ); } } self.initialLoad=NO; }


UIBezierPath para manejar el recorte del agujero transparente. El siguiente código va a una subclase de UIView que desea dibujar un agujero transparente:

- (void)drawRect:(CGRect)rect { [super drawRect:rect]; CGContextRef context = UIGraphicsGetCurrentContext(); // Clear any existing drawing on this view // Remove this if the hole never changes on redraws of the UIView CGContextClearRect(context, self.bounds); // Create a path around the entire view UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:self.bounds]; // Your transparent window. This is for reference, but set this either as a property of the class or some other way CGRect transparentFrame; // Add the transparent window UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:transparentFrame cornerRadius:5.0f]; [clipPath appendPath:path]; // NOTE: If you want to add more holes, simply create another UIBezierPath and call [clipPath appendPath:anotherPath]; // This sets the algorithm used to determine what gets filled and what doesn''t clipPath.usesEvenOddFillRule = YES; // Add the clipping to the graphics context [clipPath addClip]; // set your color UIColor *tintColor = [UIColor blackColor]; // (optional) set transparency alpha CGContextSetAlpha(context, 0.7f); // tell the color to be a fill color [tintColor setFill]; // fill the path [clipPath fill]; }