Ios 使用核心图形绘制时如何跳过区域(剪切)

Ios 使用核心图形绘制时如何跳过区域(剪切),ios,swift,uikit,core-graphics,Ios,Swift,Uikit,Core Graphics,我想画一些项目,但留下一个真正的阿尔法透明度剪切为圆形区域。我想要达到的目标: 黄色是示例背景,用于显示出血 剪切宽度实际上比圆弧笔划宽,因此它们不会完全相交。我需要真正的剪切,因为我需要保存到一个透明的图像 我想也许我可以使用setBlendMode(),但我相信只有当我希望剪切的宽度与圆弧的宽度完全相同时,这种方法才会起作用。但我想说的要点是: 随后是一份快速的工作手册。任何关于实现这一点的提示都将不胜感激 import Foundation import UIKit var dime

我想画一些项目,但留下一个真正的阿尔法透明度剪切为圆形区域。我想要达到的目标:

黄色是示例背景,用于显示出血

剪切宽度实际上比圆弧笔划宽,因此它们不会完全相交。我需要真正的剪切,因为我需要保存到一个透明的图像

我想也许我可以使用setBlendMode(),但我相信只有当我希望剪切的宽度与圆弧的宽度完全相同时,这种方法才会起作用。但我想说的要点是:

随后是一份快速的工作手册。任何关于实现这一点的提示都将不胜感激

import Foundation
import UIKit

var dimen: CGFloat = 200.0;
var strokeWidth: CGFloat = 20.0;
var cutoutWidth: CGFloat = 30.0;

class DonutView : UIView
{
    override func draw(_ rect: CGRect)
    {

        // cutout
        let cutoutColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
        cutoutColor.setFill()
        let cutoutPath = UIBezierPath(ovalIn: CGRect(x: dimen-cutoutWidth, y: dimen/2-cutoutWidth/2, width: cutoutWidth, height: cutoutWidth))
        cutoutPath.fill()

//        let context = UIGraphicsGetCurrentContext()!
//        context.setBlendMode(.sourceOut)

        let ringOffset = cutoutWidth/2;
        let circleWidth = dimen - ringOffset*2;

        // ring
        let ringPath = UIBezierPath(ovalIn: CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth))
        let ringColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3)
        ringColor.setStroke()
        ringPath.lineWidth = strokeWidth
        ringPath.stroke()

        // arc
        let arcRect = CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth)
        let arcPath = UIBezierPath()
        arcPath.addArc(withCenter: CGPoint(x: arcRect.midX, y: arcRect.midY), radius: arcRect.width / 2, startAngle: -90 * CGFloat.pi/180, endAngle: 37 * CGFloat.pi/180, clockwise: true)

        let arcColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
        arcColor.setStroke()
        arcPath.lineWidth = strokeWidth
        arcPath.stroke()
    }
}

var view = DonutView(frame: CGRect.init(x: 0, y: 0, width: dimen, height: dimen))
view.backgroundColor = UIColor.yellow

// View these elements
view
(编辑:我应该先说明这一点:这是为了最终为WatchKit创建UIImage)

借助


您可以使用另一个
CAShapeLayer
作为掩码来执行此操作

遮罩层alpha=1.0的部分将完全透明

所以

如果我们将Arc层作为环形层的子层,则可以将剪切层应用为遮罩,从而产生:

以下是游乐场页面的来源:

class MyDonutView : UIView
{

    let ringLayer = CAShapeLayer()
    let arcLayer = CAShapeLayer()
    let cutoutLayer = CAShapeLayer()

    var strokeWidth: CGFloat = 20.0;
    var cutoutWidth: CGFloat = 30.0;

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit() -> Void {

        // add arcLayer as a sublayer of ringLayer
        ringLayer.addSublayer(arcLayer)

        // add ringLayer as a sublayer of self.layer
        layer.addSublayer(ringLayer)

        // ring layer stroke is black at 0.3 alpha, fill is clear
        ringLayer.strokeColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.3).cgColor
        ringLayer.fillColor = UIColor.clear.cgColor
        ringLayer.lineWidth = strokeWidth

        // arc layer stroke is black at 0.6 alpha, fill is clear
        arcLayer.strokeColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.6).cgColor
        arcLayer.lineWidth = strokeWidth
        arcLayer.fillColor = UIColor.clear.cgColor

        // cutout layer stroke is black (although we're using Zero line width
        //  fill is black
        cutoutLayer.strokeColor = UIColor.red.cgColor
        cutoutLayer.lineWidth = 0
        cutoutLayer.fillColor = UIColor.red.cgColor

    }

    override func layoutSubviews() {
        super.layoutSubviews()

        // define the "padding" around the ring
        let ringOffset = cutoutWidth / 2.0

        // define the diameter of the ring
        let circleWidth = bounds.size.width - cutoutWidth;

        // ring path
        let ringPath = UIBezierPath(ovalIn: CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth))

        // arc path
        let arcRect = CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth)
        let arcPath = UIBezierPath()
        arcPath.addArc(withCenter: CGPoint(x: arcRect.midX, y: arcRect.midY), radius: arcRect.width / 2, startAngle: -90 * CGFloat.pi/180, endAngle: 37 * CGFloat.pi/180, clockwise: true)

        // set ring layer path
        ringLayer.path = ringPath.cgPath

        // set arc layer path
        arcLayer.path = arcPath.cgPath

        // create a rect path the full size of bounds of self
        let fullPath = UIBezierPath(rect: bounds)

        // create a cutout path (the small circle to cut-out of the ring/arc)
        let cutoutPath = UIBezierPath(ovalIn: CGRect(x: bounds.size.width-cutoutWidth, y: bounds.size.width/2-cutoutWidth/2, width: cutoutWidth, height: cutoutWidth))

        // append the cutout path to the full rect path
        fullPath.append(cutoutPath)

        // even-odd winding rule
        cutoutLayer.fillRule = CAShapeLayerFillRule.evenOdd

        // set cutout layer path
        cutoutLayer.path = fullPath.cgPath

        // use cutout layer to mask ring layer
        ringLayer.mask = cutoutLayer
    }

}

class TestViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        // instantiate a MyDonutView
        let myDonutView = MyDonutView()

        // we can set the stroke and cutout widths here
        myDonutView.strokeWidth = 20.0
        myDonutView.cutoutWidth = 30.0

        // we're using auto-layout
        myDonutView.translatesAutoresizingMaskIntoConstraints = false

        // background color yellow to see the frame
        //myDonutView.backgroundColor = .yellow

        // otherwise, it should be clear
        myDonutView.backgroundColor = .clear

        // add as subview
        view.addSubview(myDonutView)

        // constrain centerX and centerY
        // width = 200, height = width
        NSLayoutConstraint.activate([

            myDonutView.widthAnchor.constraint(equalToConstant: 200.0),
            myDonutView.heightAnchor.constraint(equalTo: myDonutView.widthAnchor),
            myDonutView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            myDonutView.centerYAnchor.constraint(equalTo: view.centerYAnchor),

            ])

    }

}

let vc = TestViewController()
PlaygroundPage.current.liveView = vc

您可以使用另一个
CAShapeLayer
作为掩码来执行此操作

遮罩层alpha=1.0的部分将完全透明

所以

如果我们将Arc层作为环形层的子层,则可以将剪切层应用为遮罩,从而产生:

以下是游乐场页面的来源:

class MyDonutView : UIView
{

    let ringLayer = CAShapeLayer()
    let arcLayer = CAShapeLayer()
    let cutoutLayer = CAShapeLayer()

    var strokeWidth: CGFloat = 20.0;
    var cutoutWidth: CGFloat = 30.0;

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit() -> Void {

        // add arcLayer as a sublayer of ringLayer
        ringLayer.addSublayer(arcLayer)

        // add ringLayer as a sublayer of self.layer
        layer.addSublayer(ringLayer)

        // ring layer stroke is black at 0.3 alpha, fill is clear
        ringLayer.strokeColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.3).cgColor
        ringLayer.fillColor = UIColor.clear.cgColor
        ringLayer.lineWidth = strokeWidth

        // arc layer stroke is black at 0.6 alpha, fill is clear
        arcLayer.strokeColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.6).cgColor
        arcLayer.lineWidth = strokeWidth
        arcLayer.fillColor = UIColor.clear.cgColor

        // cutout layer stroke is black (although we're using Zero line width
        //  fill is black
        cutoutLayer.strokeColor = UIColor.red.cgColor
        cutoutLayer.lineWidth = 0
        cutoutLayer.fillColor = UIColor.red.cgColor

    }

    override func layoutSubviews() {
        super.layoutSubviews()

        // define the "padding" around the ring
        let ringOffset = cutoutWidth / 2.0

        // define the diameter of the ring
        let circleWidth = bounds.size.width - cutoutWidth;

        // ring path
        let ringPath = UIBezierPath(ovalIn: CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth))

        // arc path
        let arcRect = CGRect(x: ringOffset, y: ringOffset, width: circleWidth, height: circleWidth)
        let arcPath = UIBezierPath()
        arcPath.addArc(withCenter: CGPoint(x: arcRect.midX, y: arcRect.midY), radius: arcRect.width / 2, startAngle: -90 * CGFloat.pi/180, endAngle: 37 * CGFloat.pi/180, clockwise: true)

        // set ring layer path
        ringLayer.path = ringPath.cgPath

        // set arc layer path
        arcLayer.path = arcPath.cgPath

        // create a rect path the full size of bounds of self
        let fullPath = UIBezierPath(rect: bounds)

        // create a cutout path (the small circle to cut-out of the ring/arc)
        let cutoutPath = UIBezierPath(ovalIn: CGRect(x: bounds.size.width-cutoutWidth, y: bounds.size.width/2-cutoutWidth/2, width: cutoutWidth, height: cutoutWidth))

        // append the cutout path to the full rect path
        fullPath.append(cutoutPath)

        // even-odd winding rule
        cutoutLayer.fillRule = CAShapeLayerFillRule.evenOdd

        // set cutout layer path
        cutoutLayer.path = fullPath.cgPath

        // use cutout layer to mask ring layer
        ringLayer.mask = cutoutLayer
    }

}

class TestViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        // instantiate a MyDonutView
        let myDonutView = MyDonutView()

        // we can set the stroke and cutout widths here
        myDonutView.strokeWidth = 20.0
        myDonutView.cutoutWidth = 30.0

        // we're using auto-layout
        myDonutView.translatesAutoresizingMaskIntoConstraints = false

        // background color yellow to see the frame
        //myDonutView.backgroundColor = .yellow

        // otherwise, it should be clear
        myDonutView.backgroundColor = .clear

        // add as subview
        view.addSubview(myDonutView)

        // constrain centerX and centerY
        // width = 200, height = width
        NSLayoutConstraint.activate([

            myDonutView.widthAnchor.constraint(equalToConstant: 200.0),
            myDonutView.heightAnchor.constraint(equalTo: myDonutView.widthAnchor),
            myDonutView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            myDonutView.centerYAnchor.constraint(equalTo: view.centerYAnchor),

            ])

    }

}

let vc = TestViewController()
PlaygroundPage.current.liveView = vc

斯里克,谢谢你的回答!!事实上,我这样做纯粹是为了在WatchKit上创建图像,所以这是不合适的(为没有声明表示歉意…我只是更新了Q)。但我会把这个加入我的(小)技巧袋。我在回答中记录的方法对我很有效。圆滑,谢谢你的回答!!事实上,我这样做纯粹是为了在WatchKit上创建图像,所以这是不合适的(为没有声明表示歉意…我只是更新了Q)。但我会把这个加入我的(小)技巧袋。我在回答中记录的方法对我有效。