如何使用蒙版为圆形图像添加边框
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
您可以将其包含在 viewDidLayoutSubviews
或 layoutSubviews
中以确保大小始终正确。
注意:也许这种技术会使您的圆形蒙版过时 ;-)。根据经验,始终选择最简单的解决方案。
例如,如果您对 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
而不是 UIView
的 layoutSubviews
=].但基本思路是一样的。
--
顺便说一句,我将遮罩与此形状图层结合使用,因为如果您仅应用 UIView
的圆角,它可能会导致奇怪的伪像(请看下部非常细的灰线圆形边框):
如果您使用贝塞尔路径方法,则不会产生此类伪像:
对于 Swift 2.3 示例,请参阅 earlier revision of this answer。
这是我的尝试:
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
您可以将其包含在 viewDidLayoutSubviews
或 layoutSubviews
中以确保大小始终正确。
注意:也许这种技术会使您的圆形蒙版过时 ;-)。根据经验,始终选择最简单的解决方案。
例如,如果您对 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
而不是 UIView
的 layoutSubviews
=].但基本思路是一样的。
--
顺便说一句,我将遮罩与此形状图层结合使用,因为如果您仅应用 UIView
的圆角,它可能会导致奇怪的伪像(请看下部非常细的灰线圆形边框):
如果您使用贝塞尔路径方法,则不会产生此类伪像:
对于 Swift 2.3 示例,请参阅 earlier revision of this answer。