ios 在 UIView 中切透明孔
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9711248/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Cut transparent hole in UIView
提问by tiltem
Looking to create a view that has a transparent frame inside of it so that the views behind the view can be seen through this transparent frame, but areas outside of this will not show through. So essentially a window within the view.
希望创建一个内部具有透明框架的视图,以便可以通过此透明框架看到视图后面的视图,但不会显示外部区域。所以本质上是视图中的一个窗口。
Hoping to be able to do something like this:
希望能够做这样的事情:
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);
but the clear does not override the black so whole background is black. Any ideas along these lines?
但清晰不会覆盖黑色,所以整个背景都是黑色的。有什么想法吗?
回答by Lefteris
This is my implementation (as I did needed a view with transparent parts):
这是我的实现(因为我确实需要一个带有透明部分的视图):
Header (.h) file:
头文件 (.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
Implementation (.m) file:
实现 (.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
Now to add a view with partial transparency, you need to import the PartialTransparentView custom UIView subclass, then use it as follows:
现在要添加部分透明的视图,需要导入 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];
This will create a view with 2 transparent rects. Of course you can add as many rects as you wish, or just use one. The above code is only handling rectangles, so if you wish to use circles, you will have to modify it.
这将创建一个带有 2 个透明矩形的视图。当然,您可以根据需要添加任意数量的矩形,或者只使用一个。上面的代码只处理矩形,所以如果你想使用圆形,你必须修改它。
回答by Mosib Asad
Lefteris Answer is absolutely right, however, it creates transparent Rects. For CIRCULAR transparent layer, modify draw rect as
Lefteris Answer 是绝对正确的,但是,它创建了透明的 Rects。对于圆形透明层,修改绘制矩形为
- (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);
}
}
}
回答by sansa
Another solution: The Big rect is all the view (yellow color) and the small is the transparent rect. The color opacity is settable.
另一种解决方案:大矩形是所有视图(黄色),小矩形是透明矩形。颜色不透明度是可设置的。
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)
回答by mikeho
I used UIBezierPath
to handle cutting out the transparent hole.
The following code goes into a subclass of the UIView
that you want to draw a transparent hole:
我曾经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];
}
回答by Bushra Shahid
@mosib's answer was a lot of help for me until I wanted to draw more than one circular cutouts in my view. After struggling a little bit, I updated my drawRect like this (code in swift...sorry bad editing):
@mosib 的回答对我有很大帮助,直到我想在我的视野中绘制多个圆形切口。经过一番挣扎后,我像这样更新了我的 drawRect(快速代码......抱歉编辑不好):
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
}
回答by MaheshShanbhag
This will do the clipping:
这将进行剪辑:
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);
}
回答by Joel Márquez
Implementation of the @Lefterisansweron 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)
}
}
}
回答by dichen
This implementation supports Rectangles and Circles, written in swift: PartialTransparentMaskView
这个实现支持 Rectangles 和 Circles,用 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);
}
}
}
}
}
回答by wils
Here is my general swift implementation.
这是我的一般快速实施。
- For static views add tuples to the holeViews array as (theView, isRound)
- If you want to dynamically assign the views as I needed to, set the generator to something, say maybe
{someViewArray.map{($0,false)}} // array of views, not round
- Use the view's corner radius instead of the isRound flag if you want, isRound is just easier for making circles.
- Note that isRound is really isEllipseThatWillBeRoundIfTheViewIsSquare
- Most code won't need the public's/internal's.
- 对于静态视图,将元组添加到holeViews 数组中作为(theView, isRound)
- 如果您想根据需要动态分配视图,请将生成器设置为某些内容,例如可能
{someViewArray.map{($0,false)}} // array of views, not round
- 如果需要,请使用视图的圆角半径而不是 isRound 标志,isRound 更容易制作圆形。
- 请注意,isRound 确实是 isEllipseThatWillBeRoundIfTheViewIsSquare
- 大多数代码不需要公共/内部的。
Hope it helps someone, thanks to the other contributors
希望它可以帮助某人,感谢其他贡献者
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)
}
}
}
}
}
回答by Nick Yap
If you want something quick and effective, I added a library (TAOverlayView) to CocoaPods that allows you to create overlays with rectangular/circular holes, allowing the user to interact with views behind the overlay. I used it to create this tutorial for one of our apps:
如果你想要快速有效的东西,我向CocoaPods添加了一个库 ( TAOverlayView),它允许你创建带有矩形/圆形孔的叠加层,允许用户与叠加层后面的视图进行交互。我用它为我们的一个应用程序创建了本教程:
You can change the background by setting the backgroundColor
of the overlay with something like UIColor(red: 0, green: 0, blue: 0, alpha: 0.85)
, depending on your color and opaqueness needs.
您可以根据您的颜色和不透明度需求,backgroundColor
通过使用类似的设置覆盖层的来更改背景UIColor(red: 0, green: 0, blue: 0, alpha: 0.85)
。