Swift UIBezierPath:如何在具有圆角的视图周围添加边框?
我正在使用UIBezierPath使imageview具有圆角,但我还想为imageview添加边框。请记住,顶部是uiimage,底部是标签 当前使用此代码生成:Swift UIBezierPath:如何在具有圆角的视图周围添加边框?,swift,uibezierpath,Swift,Uibezierpath,我正在使用UIBezierPath使imageview具有圆角,但我还想为imageview添加边框。请记住,顶部是uiimage,底部是标签 当前使用此代码生成: let rectShape = CAShapeLayer() rectShape.bounds = myCell2.NewFeedImageView.frame rectShape.position = myCell2.NewFeedImageView.center rectShape.path = UIBezierPath(rou
let rectShape = CAShapeLayer()
rectShape.bounds = myCell2.NewFeedImageView.frame
rectShape.position = myCell2.NewFeedImageView.center
rectShape.path = UIBezierPath(roundedRect: myCell2.NewFeedImageView.bounds,
byRoundingCorners: .TopRight | .TopLeft,
cornerRadii: CGSize(width: 25, height: 25)).CGPath
myCell2.NewFeedImageView.layer.mask = rectShape
我想添加一个绿色边框,但我不能使用
myCell2.NewFeedImageView.layer.borderWidth = 8
myCell2.NewFeedImageView.layer.borderColor = UIColor.greenColor().CGColor
因为它切断了边框的左上角和右上角,如图所示:
有没有办法在我当前的代码中添加带有UIBezierPath的边框?当然有!每个视图都有一个
图层
属性(通过给图层圆角可以知道)。层
上的另外两个属性是borderColor
和borderWidth
。只需设置这些,您就可以在视图中添加边框!(边框将跟随圆角。)确保使用UIColor.CGColor
作为borderColor
的普通UIColor
与类型不匹配。您可以重用UIBezierPath路径,并向视图添加形状层。下面是视图控制器内部的一个示例
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Create a view with red background for demonstration
let v = UIView(frame: CGRectMake(0, 0, 100, 100))
v.center = view.center
v.backgroundColor = UIColor.redColor()
view.addSubview(v)
// Add rounded corners
let maskLayer = CAShapeLayer()
maskLayer.frame = v.bounds
maskLayer.path = UIBezierPath(roundedRect: v.bounds, byRoundingCorners: .TopRight | .TopLeft, cornerRadii: CGSize(width: 25, height: 25)).CGPath
v.layer.mask = maskLayer
// Add border
let borderLayer = CAShapeLayer()
borderLayer.path = maskLayer.path // Reuse the Bezier path
borderLayer.fillColor = UIColor.clearColor().CGColor
borderLayer.strokeColor = UIColor.greenColor().CGColor
borderLayer.lineWidth = 5
borderLayer.frame = v.bounds
v.layer.addSublayer(borderLayer)
}
}
最终结果是这样的
请注意,只有当视图的大小固定时,此选项才能按预期工作。当视图可以调整大小时,您需要创建一个自定义视图类,并在布局子视图中调整图层大小,如上所述:
要做到完美并不容易。
这是一个简单的解决方案
这个
- 正确解决您正在绘制边界线一半的问题
- 在自动布局中完全可用
- 当视图大小更改或设置动画时,完全重新工作
- 是完全可设计的-你可以在你的故事板上实时看到它
2019年
@IBDesignable
class RoundedCornersAndTrueBorder: UIView {
@IBInspectable var cornerRadius: CGFloat = 10 {
didSet { setup() }
}
@IBInspectable var borderColor: UIColor = UIColor.black {
didSet { setup() }
}
@IBInspectable var trueBorderWidth: CGFloat = 2.0 {
didSet { setup() }
}
override func layoutSubviews() {
setup()
}
var border:CAShapeLayer? = nil
func setup() {
// make a path with round corners
let path = UIBezierPath(
roundedRect: self.bounds, cornerRadius:cornerRadius)
// note that it is >exactly< the size of the whole view
// mask the whole view to that shape
// note that you will ALSO be masking the border we'll draw below
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
// add another layer, which will be the border as such
if (border == nil) {
border = CAShapeLayer()
self.layer.addSublayer(border!)
}
// IN SOME APPROACHES YOU would INSET THE FRAME
// of the border-drawing layer by the width of the border
// border.frame = bounds.insetBy(dx: borderWidth, dy: borderWidth)
// so that when you draw the line, ALL of the WIDTH of the line
// DOES fall within the actual mask.
// here, we will draw the border-line LITERALLY ON THE EDGE
// of the path. that means >HALF< THE LINE will be INSIDE
// the path and HALF THE LINE WILL BE OUTSIDE the path
border!.frame = bounds
let pathUsingCorrectInsetIfAny =
UIBezierPath(roundedRect: border!.bounds, cornerRadius:cornerRadius)
border!.path = pathUsingCorrectInsetIfAny.cgPath
border!.fillColor = UIColor.clear.cgColor
// the following is not what you want:
// it results in "half-missing corners"
// (note however, sometimes you do use this approach):
//border.borderColor = borderColor.cgColor
//border.borderWidth = borderWidth
// this approach will indeed be "inside" the path:
border!.strokeColor = borderColor.cgColor
border!.lineWidth = trueBorderWidth * 2.0
// HALF THE LINE will be INSIDE the path and HALF THE LINE
// WILL BE OUTSIDE the path. so MAKE IT >>TWICE AS THICK<<
// as requested by the consumer class.
}
}
@IBDesignable
类RoundCorners和TrueBorder:UIView{
@i可检测的var转角半径:CGFloat=10{
didSet{setup()}
}
@IBInspectable var borderColor:UIColor=UIColor.black{
didSet{setup()}
}
@IBInspectable变量trueBorderWidth:CGFloat=2.0{
didSet{setup()}
}
覆盖func布局子视图(){
设置()
}
var边界:CAShapeLayer?=零
函数设置(){
//做一条有圆角的路
let path=UIBezierPath(
roundedRect:self.bounds,cornerRadius:cornerRadius)
//请注意,它>正好<整个视图的大小
//将整个视图遮罩为该形状
//请注意,您还将屏蔽我们将在下面绘制的边界
let mask=CAShapeLayer()
mask.path=path.cgPath
self.layer.mask=掩码
//添加另一层,该层将作为边界
如果(边框==nil){
border=CAShapeLayer()
self.layer.addSublayer(边框!)
}
//在某些方法中,您可以插入框架
//按边框宽度设置边框绘制图层的宽度
//border.frame=bounds.insetBy(dx:borderWidth,dy:borderWidth)
//所以当你画这条线的时候,线的所有宽度
//不在实际遮罩范围内。
//在这里,我们将在边缘上绘制边界线
//这意味着>一半<线将在内部
//这条路和半条线将在这条路的外面
边框!.frame=bounds
让PathUsingCorrectInsertifany=
UIBezierPath(roundedRect:border!.bounds,cornerRadius:cornerRadius)
border!.path=PathUsingCorrectInsertifany.cgPath
border!.fillColor=UIColor.clear.cgColor
//以下不是您想要的:
//它会导致“半缺角”
//(但请注意,有时您确实使用这种方法):
//border.borderColor=borderColor.cgColor
//border.borderWidth=borderWidth
//这种方法确实是“内部”路径:
border!.strokeColor=borderColor.cgColor
边框!.lineWidth=trueBorderWidth*2.0
//半条线将在路径内,半条线
//将在路径之外。因此,使其>>两倍于绝对完美的2019解决方案
不用再多说了,下面就是你如何做到这一点的
不要实际使用视图附带的“基本”层
仅为图像创建一个新层。现在可以(循环)遮罩该层而不影响下一层
为边界创建一个新层,这样它就不会被图片层屏蔽
关键事实是
使用CALayer,您确实可以应用.mask,它只影响该层
在绘制圆(或任何边界)时,必须非常小心地注意这样一个事实,即您只能获得“一半宽度”——简言之,从不使用与绘制的路径相同的进行裁剪
请注意,原始cat图像的宽度与水平黄色箭头的宽度完全相同。您必须小心地绘制图像,以便整个图像显示在圆形中,小于整个自定义控件
所以,以通常的方式进行设置
import UIKit
@IBDesignable class GreenCirclePerson: UIView {
@IBInspectable var borderColor: UIColor = UIColor.black { didSet { setup() } }
@IBInspectable var trueBorderThickness: CGFloat = 2.0 { didSet { setup() } }
@IBInspectable var trueGapThickness: CGFloat = 2.0 { didSet { setup() } }
@IBInspectable var picture: UIImage? = nil { didSet { setup() } }
override func layoutSubviews() { setup() }
var imageLayer: CALayer? = nil
var border: CAShapeLayer? = nil
func setup() {
if (imageLayer == nil) {
imageLayer = CALayer()
self.layer.addSublayer(imageLayer!)
}
if (border == nil) {
border = CAShapeLayer()
self.layer.addSublayer(border!)
}
现在,仔细制作圆形裁剪图像的图层:
// the ultimate size of our custom control:
let box = self.bounds.aspectFit()
let totalInsetOnAnyOneSide = trueBorderThickness + trueGapThickness
let boxInWhichImageSits = box.inset(by:
UIEdgeInsets(top: totalInsetOnAnyOneSide, left: totalInsetOnAnyOneSide,
bottom: totalInsetOnAnyOneSide, right: totalInsetOnAnyOneSide))
// just a note. that version of inset#by is much clearer than the
// confusing dx/dy variant, so best to use that one
imageLayer!.frame = boxInWhichImageSits
imageLayer!.contents = picture?.cgImage
imageLayer?.contentsGravity = .resizeAspectFill
let halfImageSize = boxInWhichImageSits.width / 2.0
let maskPath = UIBezierPath(roundedRect: imageLayer!.bounds,
cornerRadius:halfImageSize)
let maskLayer = CAShapeLayer()
maskLayer.path = maskPath.cgPath
imageLayer!.mask = maskLayer
接下来,作为一个完全独立的层,根据需要绘制边界:
// now create the border
border!.frame = bounds
// To draw the border, you must inset it by half the width of the border,
// otherwise you'll be drawing only half the border. (Indeed, as an additional
// subtle problem you are clipping rather than rendering the outside edge.)
let halfWidth = trueBorderThickness / 2.0
let borderCenterlineBox = box.inset(by:
UIEdgeInsets(top: halfWidth, left: halfWidth,
bottom: halfWidth, right: halfWidth))
let halfBorderBoxSize = borderCenterlineBox.width / 2.0
let borderPath = UIBezierPath(roundedRect: borderCenterlineBox,
cornerRadius:halfBorderBoxSize)
border!.path = borderPath.cgPath
border!.fillColor = UIColor.clear.cgColor
border!.strokeColor = borderColor.cgColor
border!.lineWidth = trueBorderThickness
}
}
在iOS标准控件中,一切都可以完美运行:
所有看不见的东西都是看不见的;您可以通过整个自定义控件看到后面的任何材质,没有“半厚度”问题或缺少图像材质,您可以按常规方式设置自定义控件背景颜色等。检查器控件都能正常工作。(呸!)
类似的解决方案:
-图像+圆形+阴影
-双角问题
- "