在UIView中切透明的洞

想要创build一个内部具有透明框架的视图,以便通过这个透明框架可以看到视图背后的视图,但是在这之外的区域不会显示出来。 所以基本上是视图内的一个窗口。

希望能够做到这样的事情:

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); 

但清除不会覆盖黑色,所以整个背景是黑色的。 任何想法沿着这些线?

这是我的实现(因为我需要透明部分的视图):

标题(.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 

实施(.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 

现在要添加一个具有部分透明度的视图,您需要导入PartialTransparentView自定义UIView子类,然后按如下所示使用它:

 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]; 

这将创build一个2透明rects视图。 当然,你可以根据需要添加尽可能多的rects,或者只使用一个。 上面的代码只是处理矩形,所以如果你想使用圆形,你将不得不修改它。

Lefteris答案是绝对正确的,但是,它创造了透明Rects。 对于CIRCULAR透明图层,修改绘制矩形为

 - (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); } } } 

我用UIBezierPath来处理透明的洞。 下面的代码进入UIView的子类,你想绘制一个透明的洞:

 - (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]; } 

这将做剪辑:

 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); } 

@ mosib的回答对我来说是一个很大的帮助,直到我想在我看来要画出多个圆形切口。 在挣扎一点之后,我更新了我的drawRect(swift中的代码…对不起,不好编辑):

 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 } 

另一个解决scheme:大矩形是所有视图(黄色),小矩形是透明矩形。 颜色不透明度是可设置的。

 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) 

在这里输入图像说明

这个实现支持用swift编写的矩形和圆形: 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); } } } } } 

这是我一般的快速实施。

  • 对于静态视图,将元组添加到holeViews数组中作为(theView,isRound)
  • 如果你想dynamic分配的意见,我需要设置生成器的东西,说可能{someViewArray.map{($0,false)}} // array of views, not round
  • 如果需要,可以使用视图的圆angular半径来代替isRound标志,isRound只是用于制作圆圈。
  • 请注意,isRound确实是EllipseThatWillBeRoundIfTheViewIsSquare
  • 大多数代码不需要公共/内部的。

希望它能帮助别人,这要归功于其他贡献者

 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) } } } } } 

如果你想要一些快速而有效的东西,我给CocoaPods添加了一个库( TAOverlayView ),它允许你创build带有矩形/圆形孔的覆盖层,允许用户与覆盖层后面的视图进行交互。 我用它为我们的一个应用程序创build了这个教程:

使用TAOverlayView的教程

您可以通过使用UIColor(red: 0, green: 0, blue: 0, alpha: 0.85)等设置覆盖的backgroundColor来更改背景,具体取决于您的颜色和不透明度需求。

那么我将不得不回答,因为错过了评论,并填写了一个答案表:)我真的很喜欢Carsten提供更多的信息,以最好的方式来做他的build议。

你可以使用

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

创造任何复杂的背景“色彩”形象。 如果您熟悉绘图类,则可以以编程方式创build图像,或者如果预定义了窗口框架,则可以静态地创build图像。

结束了“伪装”

windowFrame是一个属性

 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); 

在这个代码中创build的不止是圆

 - (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; } 

包括使用C#的Xamarin Studio iOS的答案。 这将绘制一个带有60%Alpha的单个圆angular矩形。 大多来自@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(); } 

反过来做! 将您希望通过“洞”看到的视图放在适当大小的单独视图中。 然后将“clipsToBounds”设置为YES并将该视图置于顶部。 那么“透明”的框架是最底下的。 “clipsToBounds”意味着盒子/洞外的一切都被切断。

那么你可能不得不处理如何处理触摸。 但这是另一个问题。 也许在相应的视图上设置userInteractionEnabled就足够了。