想要创建一个在其中具有透明框架的视图,以便可以通过此透明框架看到视图后面的视图,但是此外的区域将无法显示。所以在视图中基本上是一个窗口。
希望能够做到这样的事情:
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];
这将创建一个包含2个透明rects的视图。当然,您可以根据需要添加任意数量的rects,或者只使用一个。上面的代码只处理矩形,所以如果你想使用圆圈,你将不得不修改它。
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)
}
}
}
好吧,我将不得不回答错过评论并填写答案表:)我真的希望Carsten提供更多关于他提出的建议的最佳方式的信息。
你可以用
+ (UIColor *)colorWithPatternImage:(UIImage *)image
创建任何复杂性的背景“颜色”图像。如果您熟悉绘图类,则可以以编程方式创建图像,如果预定义了Windows框架,则可以静态创建图像。
结束了“假装”它
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);
在此代码中创建多个圆圈
- (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的单个圆角矩形。主要取自@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();
}
您可以通过为视图的图层指定边框来实现此目的。
class HollowSquareView: UIView {
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = UIColor.clear
self.layer.masksToBounds = true
self.layer.borderColor = UIColor.black.cgColor
self.layer.borderWidth = 10.0
}
}
这将为您提供宽度为10的方形框架和透明的核心。
您还可以将图层的cornerRadius
设置为视图宽度的一半,这将为您提供一个空心圆。
我使用了Bushra Shahid的answer并且效果很好,但如果圆圈相互重叠则会有问题。
我使用了这种不同的方法,在这种情况下效果很好:
class HoleView: UIView {
var holes: [CGRect] = [] {
didSet {
lastProcessedSize = .zero
createMask()
}
}
private var lastProcessedSize = CGSize.zero
override func layoutSubviews() {
super.layoutSubviews()
createMask()
}
private func createMask() {
guard lastProcessedSize != frame.size,
holes.count > 0
else { return }
let size = frame.size
// create image
UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
guard let context = UIGraphicsGetCurrentContext()
else { return }
UIColor.white.setFill()
context.fill(CGRect(origin: .zero, size: size))
UIColor.black.setFill()
holes.forEach { context.fillEllipse(in: $0) }
// apply filter to convert black to transparent
guard let image = UIGraphicsGetImageFromCurrentImageContext(),
let cgImage = image.cgImage,
let filter = CIFilter(name: "CIMaskToAlpha")
else { return }
filter.setDefaults()
filter.setValue(CIImage(cgImage: cgImage), forKey: kCIInputImageKey)
guard let result = filter.outputImage,
let cgMaskImage = CIContext().createCGImage(result, from: result.extent)
else { return }
// Create mask
let mask = CALayer()
mask.frame = bounds
mask.contents = cgMaskImage
layer.mask = mask
}
}
综上所述:
UIImage
面具而不是/透明。CIMaskToAlpha
CIFilter
将其转换为透明/白色面具。CGImage
作为CALayer
的内容CALayer
视为视图蒙版。反过来做吧!将您想要通过“洞”看到的视图放在正确大小的单独视图中。然后将“clipsToBounds”设置为YES并将该视图置于顶部。具有“透明”框架的视图是最重要的。 “clipsToBounds”表示盒子/孔外的所有东西都被切掉了。
那么你可能不得不处理触摸的处理方式。但这是另一个问题。也许在相应的视图上设置userInteractionEnabled就足够了。
Lefteris答案是绝对正确的,但它会创建透明的Rects。对于CIRCULAR透明层,将draw rect修改为
- (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];
}
@ 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
}
另一个解决方案:大矩形是所有视图(黄色),小是透明矩形。颜色不透明度是可设置的。
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)
这将做剪辑:
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);
}
这个实现支持用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);
}
}
}
}
}
这是我的一般快速实施。
{someViewArray.map{($0,false)}} // array of views, not round
希望它可以帮助某人,感谢其他贡献者
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),允许你创建带有矩形/圆形孔的叠加层,允许用户与叠加层后面的视图进行交互。我用它为我们的一个应用程序创建了这个教程:
您可以通过使用backgroundColor
等设置叠加层的UIColor(red: 0, green: 0, blue: 0, alpha: 0.85)
来更改背景,具体取决于您的颜色和不透明度需求。