如何使用蒙版为圆形图像添加边框

How to add a border to a circular image with mask

这是我的尝试:

func round() {
    let width = bounds.width < bounds.height ? bounds.width : bounds.height
    let mask = CAShapeLayer()
    mask.path = UIBezierPath(ovalInRect: CGRectMake(bounds.midX - width / 2, bounds.midY - width / 2, width, width)).CGPath

    self.layer.mask = mask

    // add border
    let frameLayer = CAShapeLayer()
    frameLayer.path = mask.path
    frameLayer.lineWidth = 4.0
    frameLayer.strokeColor = UIColor.whiteColor().CGColor
    frameLayer.fillColor = nil

    self.layer.addSublayer(frameLayer)
}

它适用于 iphone 6 模拟器(故事板的大小为 4.7),但在 5s 和 6+ 上它看起来很奇怪:

这是自动布局问题吗?没有边框,自动布局工作正常。这是我第一次使用面具,所以我不确定我所做的是否正确。

round 函数在 viewDidLayoutSubviews 中被调用。

有什么想法吗?

最简单的方法是操纵图像视图本身的 layer

imageView.layer.cornerRadius = imageView.bounds.size.width / 2.0
imageView.layer.borderWidth = 2.0
imageView.layer.borderColor = UIColor.whiteColor.CGColor
imageView.layer.masksToBounds = true

您可以将其包含在 viewDidLayoutSubviewslayoutSubviews 中以确保大小始终正确。

注意:也许这种技术会使您的圆形蒙版过时 ;-)。根据经验,始终选择最简单的解决方案。

例如,如果您对 UIImageView 进行了子类化,您可以重写 layoutSubviews,以便它 (a) 更新掩码​​; (b) 删除任何旧边界; (c) 增加一个新的边界。在 Swift 3:

import UIKit

@IBDesignable
class RoundedImageView: UIImageView {

    /// saved rendition of border layer

    private weak var borderLayer: CAShapeLayer?

    override func layoutSubviews() {
        super.layoutSubviews()

        // create path

        let width = min(bounds.width, bounds.height)
        let path = UIBezierPath(arcCenter: CGPoint(x: bounds.midX, y: bounds.midY), radius: width / 2, startAngle: 0, endAngle: .pi * 2, clockwise: true)

        // update mask and save for future reference

        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask

        // create border layer

        let frameLayer = CAShapeLayer()
        frameLayer.path = path.cgPath
        frameLayer.lineWidth = 32.0
        frameLayer.strokeColor = UIColor.white.cgColor
        frameLayer.fillColor = nil

        // if we had previous border remove it, add new one, and save reference to new one

        borderLayer?.removeFromSuperlayer()
        layer.addSublayer(frameLayer)
        borderLayer = frameLayer
    }
}

这样,它会响应布局的更改,但它会确保清除所有旧边框。

顺便说一下,如果您不是子类化 UIImageView,而是将此逻辑放入视图控制器中,您将覆盖 viewWillLayoutSubviews 而不是 UIViewlayoutSubviews =].但基本思路是一样的。

--

顺便说一句,我将遮罩与此形状图层结合使用,因为如果您仅应用 UIView 的圆角,它可能会导致奇怪的伪像(请看下部非常细的灰线圆形边框):

如果您使用贝塞尔路径方法,则不会产生此类伪像:

对于 Swift 2.3 示例,请参阅 earlier revision of this answer