Ios 带透明孔的滤筒

Ios 带透明孔的滤筒,ios,objective-c,calayer,quartz-core,Ios,Objective C,Calayer,Quartz Core,我有一个简单的视图(图片的左侧),我需要为这个视图创建某种覆盖(图片的右侧)。此覆盖应该有一些不透明度,因此它下面的视图仍然部分可见。 最重要的是,这个覆盖物应该在它的中间有一个圆孔,这样它就不会覆盖视图的中心(参见图片波纹管)。p> 我可以很容易地创建这样一个圆: int radius = 20; //whatever CAShapeLayer *circle = [CAShapeLayer layer]; circle.path = [UIBezierPath bezierPathWith

我有一个简单的视图(图片的左侧),我需要为这个视图创建某种覆盖(图片的右侧)。此覆盖应该有一些不透明度,因此它下面的视图仍然部分可见。 最重要的是,这个覆盖物应该在它的中间有一个圆孔,这样它就不会覆盖视图的中心(参见图片波纹管)。p> 我可以很容易地创建这样一个圆:

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;
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];
和一个“完整”的矩形覆盖,如下所示:

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;
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];
但我不知道如何将这两层结合起来,使它们产生我想要的效果。任何人我什么都试过了。。。非常感谢你的帮助


我通过乔恩·施泰因梅茨的建议解决了这个问题。如果有人在乎,这里是最终的解决方案:

Swift 3.x:

Swift 4.2和5:

let radius:CGFloat=myRect.size.width
让path=UIBezierPath(roundedRect:CGRect(x:0,y:0,width:self.view.bounds.size.width,height:self.view.bounds.size.height),cornerRadius:0)
设circlePath=UIBezierPath(roundedRect:CGRect(x:0,y:0,宽度:2*半径,高度:2*半径),拐角半径:半径)
append(circlePath)
path.usesevenodfillrule=true
设fillLayer=CAShapeLayer()
fillLayer.path=path.cgPath
fillLayer.fillRule=.evenOdd
fillLayer.fillColor=view.backgroundColor?.cgColor
fillLayer.opacity=0.5
view.layer.addSublayer(fillLayer)

我采用了与animal_chin类似的方法,但我更直观,所以我使用Outlet和auto layout在Interface Builder中设置了大部分内容

这是我在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

代码Swift 2.0兼容

从@animal_inch answer开始,我编写了一个小实用程序类,希望它能欣赏:

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

}
然后,在代码中的任何位置:

let circle = CircleMaskView(drawIn: <target_view>)
circle.opacity = 0.7
circle.draw()
let circle=CircleMaskView(绘图:)
圆形。不透明度=0.7
画圆

要创建这种效果,我发现最简单的方法是创建覆盖屏幕的整个视图,然后使用图层和UIBezier路径减去部分屏幕。为了迅速实施:

// 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)
我测试了上面的代码,结果如下:

我在CocoaPods中添加了一个库,它抽象了上面的许多代码,并允许您轻松创建带有矩形/圆形孔的覆盖层,允许用户与覆盖层后面的视图交互。我使用它为我们的一个应用程序创建了本教程:


该库被调用,并且是Apache2.0下的开源库。我希望你觉得它有用

公认的解决方案Swift 3.0兼容

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)

你能创建一个包含矩形和圆的贝塞尔曲线吗?然后在绘图过程中使用的缠绕规则将创建一个孔(我没有尝试过)。我不知道如何操作:)使用矩形创建,然后
移动点
,然后添加圆形矩形。查看文档以了解
UIBezierPath
提供的方法。查看类似的问答帮助:[在UIView中剪切透明孔][1][1]:在此处查看我的解决方案:希望这有助于增加灵活性,使视图子类“IBDesignable”。这真的很容易!首先,将上面的代码插入到我对这个问题的回答中:作为一名iOS新手,我花了几个小时试图弄清楚为什么这段代码会产生奇怪的结果。最后我发现,如果在某个点重新计算叠加遮罩,则必须删除添加的子层。这可以通过view.layer.sublayers属性实现。非常感谢您的回答!为什么我得到了完全相反的结果。带黑色半透明形状的透明彩色层??如何使用此模式将透明文本添加到圆中,是可能的吗?我不知道用了6年的时间,它还能起到什么作用,但请记住,这个中空的洞并不能真正“穿透”支撑它的那一层。比方说,如果把洞覆盖在按钮上。该按钮不可访问,如果您想像我一样制作“指南教程”,则需要该按钮。@Nick Yap提供的库将为您完成这项工作,其中覆盖UIView的func点(内部点:CGPoint,带有event:UIEvent?->Bool{}。更多详情,请查看他的图书馆。但是,你所期望的只是“看到面具后面的东西”,这是一个有效的答案。另外,请不要发布。相反,考虑其他行动,可以帮助未来用户找到他们需要的答案,如链接帖子中所描述的。当这些答案仅仅是一个链接和使用你的东西的推荐时,它们看起来很垃圾。@Mogsdad我不想显得垃圾,我只是在这个库上花了很多时间,我觉得它对尝试做类似事情的人很有用。但是谢谢你的反馈,我会更新我的答案以使用代码示例更新,尼克。我就在你的角落里——我自己也有已发布的库和实用程序,我明白,在我的文档已经涵盖的情况下,在这里给出完整的答案似乎是多余的。。。然而,我们的想法是让答案尽可能独立。还有人只发垃圾邮件,所以我不想和他们混在一起。我想你也有同样的想法,这就是我向你指出的原因。干杯我用了你创造的豆荚,谢谢你。但我的观点在覆盖层下停止了互动。怎么了?我有一个带有imageview的Scrollview。@AmmarMujeeb除了通过您创建的“孔”之外,覆盖层会阻止交互。我使用pod的目的是覆盖屏幕的突出部分,并且只允许您与突出显示的元素交互。我喜欢这个解决方案,因为您可以在设计时和运行时非常轻松地移动圆形路径。这对我不起作用,尽管我将其修改为使用普通矩形而不是椭圆形,但是最后一张面具图片出现了错误:(@Fattie:你的链接死了