在 UIImageView 中屏蔽甜甜圈形状

Masking a donut shape in a UIImageView

我正在尝试将甜甜圈形状切割成 UIImageView。我目前只能实现这样的圆形切割:

import UIKit
import CoreGraphics

class AvatarImageView: UIImageView {

    enum AvatarImageViewOnlineStatus {
        case online
        case offline
        case hidden
    }

    var rounded: Bool = false
    var onlineStatus: AvatarImageViewOnlineStatus = .hidden

    override func layoutSubviews() {
        super.layoutSubviews()
        guard !rounded else { return }
        if let image = image, !rounded {
            self.image = image.af_imageRoundedIntoCircle()
            rounded = true
        }

        let rect: CGRect = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
        let maskLayer: CAShapeLayer = CAShapeLayer()
        maskLayer.frame = rect

        let mainPath: UIBezierPath = UIBezierPath(rect: rect)
        let circlePath: UIBezierPath = UIBezierPath(ovalIn: CGRect(x: 20, y: 20, width: 30, height: 30))
        mainPath.append(circlePath)

        maskLayer.fillRule = kCAFillRuleEvenOdd
        maskLayer.path = mainPath.cgPath
        maskLayer.strokeColor = UIColor.clear.cgColor
        maskLayer.lineWidth = 10.0
        layer.mask = maskLayer
    }

}

是否可以使用蒙版创建圆环形状的切口?

这比我预期的要花更多的努力,解决方案并不明显......直到我找到它,然后它就像,哦,是的,当然 :/

"basic" 的想法是,您想 "cut out" 现有路径的一部分,解决方案并不明显,因为 UIBezierPath 没有提供 "subtract" 或 "remove" 方法。相反,您需要 "reverse" 路径,例如...

let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 200, height: 200))
path.append(UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 100, height: 100)).reversing())
path.append(UIBezierPath(ovalIn: CGRect(x: 75, y: 75, width: 50, height: 50)))

而且,因为 "out-of-context" 片段很少有用,这是我用来测试它的 playground(提供自己的图像)...

import UIKit
import Foundation
import CoreGraphics

let image = UIImage(named: "Profile")
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
let backgroundView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
backgroundView.backgroundColor = UIColor.green
backgroundView.addSubview(imageView)

imageView.contentMode = .scaleAspectFit
imageView.image = image
imageView.layer.cornerRadius = 100

let shapeLayer = CAShapeLayer()

let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 200, height: 200))
path.append(UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 100, height: 100)).reversing())
path.append(UIBezierPath(ovalIn: CGRect(x: 75, y: 75, width: 50, height: 50)))

shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.frame = imageView.bounds

imageView.layer.mask = shapeLayer
backgroundView

我的结果...

绿色来自 UIImageView 所在的 backgroundView

现在,如果您更喜欢...

然后去掉第二个append语句,path.append(UIBezierPath(ovalIn: CGRect(x: 75, y: 75, width: 50, height: 50)))