Как пропустить область (вырез) при рисовании с основной графикой - PullRequest
0 голосов
/ 23 сентября 2019

Я хочу нарисовать некоторые элементы, но оставлю настоящий вырез альфа-прозрачности для круглой области.Чего я хочу добиться:

image

Желтый - это пример фона, чтобы показать прокачку.

Ширина выреза на самом деле шире, чем ход дуги, поэтому они нене пересекаются.Мне нужен настоящий вырез, потому что я сохраняю изображение с прозрачностью.

Я подумал, что, возможно, смогу использовать setBlendMode (), но я верю, что это сработает, только если я хочу, чтобы мой вырез был точно такой же ширины, что и дуга.,Но есть суть того, как я пытался это сделать:

image

Далее следует книга Swift.Любые советы по достижению этого с благодарностью.

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

(Правка: я должен был заявить об этом изначально: это в конечном итоге создать UIImage для WatchKit)

Ответы [ 2 ]

1 голос
/ 24 сентября 2019

Вы можете сделать это, используя другую CAShapeLayer в качестве маски.

Часть (и) слоя маски с альфа = 1.0 будет полностью прозрачной.

Итак...

enter image description here

Если мы сделаем слой дуги подслоем слоя кольца, мы можем затем применить слой выреза в качестве маски, в результате чего:

enter image description here

Вот источник страницы игровой площадки:

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
1 голос
/ 24 сентября 2019

С помощью Как очистить кружок в CGContext в iOS

import Foundation
import UIKit
import PlaygroundSupport

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

class DonutView : UIImageView
{
    override init(frame: CGRect) {
        super.init(frame: frame)

        UIGraphicsBeginImageContextWithOptions(CGSize(width: dimen, height: dimen), false, 1)
        let context = UIGraphicsGetCurrentContext()!

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

        // Cutout circle
        context.setFillColor(UIColor.clear.cgColor)
        context.setBlendMode(.clear)        
        context.addEllipse(in: CGRect(x: dimen-cutoutWidth, y: dimen/2-cutoutWidth/2, width: cutoutWidth, height: cutoutWidth))
        context.drawPath(using: .fill)
        context.setBlendMode(.normal)

        image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()        
        }

    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

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

// View these elements
view
...