关于 ios:cornerRadius with border: Glitch around border

cornerRadius with border: Glitch around border

我的应用程序主要是基于圆形和边框的。

我使用 UIView 的 layer 属性来给出圆角半径和边框。

但我面临一个问题,角落不清晰。

我得到以下结果:

UI按钮

screenshot

UIImageView

screenshot

您可以观察到白色或灰色边框周围的细边框线。

这是我的代码:

1
2
3
4
5
button.layer.borderWidth = 2.0;
button.layer.borderColor = [[UIColor whiteColor] CGColor];
button.layer.cornerRadius = 4;

button.clipsToBounds = YES;

我已经想办法解决这个问题,但没有成功。

我试过button.layer.masksToBounds = YES,但没有效果。

我错过了什么吗?或者与 CALayer 相比,还有其他方法可以给我更好的结果吗?


我尝试了很多解决方案,最后使用 UIBezierPath.

我创建了 UIView 的类别并添加了制作圆形矩形和边框的方法。

这是该类别的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- (void)giveBorderWithCornerRadious:(CGFloat)radius borderColor:(UIColor *)borderColor andBorderWidth:(CGFloat)borderWidth
{
    CGRect rect = self.bounds;
   
    //Make round
        // Create the path for to make circle
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect
                                                       byRoundingCorners:UIRectCornerAllCorners
                                                             cornerRadii:CGSizeMake(radius, radius)];

        // Create the shape layer and set its path
        CAShapeLayer *maskLayer = [CAShapeLayer layer];

        maskLayer.frame = rect;
        maskLayer.path  = maskPath.CGPath;
       
        // Set the newly created shape layer as the mask for the view's layer
        self.layer.mask = maskLayer;
   
    //Give Border
        //Create path for border
        UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect:rect
                                                         byRoundingCorners:UIRectCornerAllCorners
                                                               cornerRadii:CGSizeMake(radius, radius)];

        // Create the shape layer and set its path
        CAShapeLayer *borderLayer = [CAShapeLayer layer];
       
        borderLayer.frame       = rect;
        borderLayer.path        = borderPath.CGPath;
        borderLayer.strokeColor = [UIColor whiteColor].CGColor;
        borderLayer.fillColor   = [UIColor clearColor].CGColor;
        borderLayer.lineWidth   = borderWidth;
       
        //Add this layer to give border.
        [[self layer] addSublayer:borderLayer];
}

我从这篇精彩的文章中了解到使用 UIBezierPath 的想法:Thinking like a B??zier path

我从这两个链接中获得了大部分代码:

  • UIView 类别仅用于舍入您想要的角,而不是像 CALayercornerRadius 一样。
  • 如何在 UIBezierPath 上获得边框

注意:这是类别方法,因此它自己表示调用该方法的视图。像 UIButton、UIImageView 等。


这是@CRDave 的 Swift 5 版本作为 UIView 的扩展:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
protocol CornerRadius {
    func makeBorderWithCornerRadius(radius: CGFloat, borderColor: UIColor, borderWidth: CGFloat)
}

extension UIView: CornerRadius {

    func makeBorderWithCornerRadius(radius: CGFloat, borderColor: UIColor, borderWidth: CGFloat) {
        let rect = self.bounds

        let maskPath = UIBezierPath(roundedRect: rect,
                                    byRoundingCorners: .allCorners,
                                    cornerRadii: CGSize(width: radius, height: radius))

        // Create the shape layer and set its path
        let maskLayer = CAShapeLayer()
        maskLayer.frame = rect
        maskLayer.path  = maskPath.cgPath

        // Set the newly created shape layer as the mask for the view's layer
        self.layer.mask = maskLayer

        // Create path for border
        let borderPath = UIBezierPath(roundedRect: rect,
                                      byRoundingCorners: .allCorners,
                                      cornerRadii: CGSize(width: radius, height: radius))

        // Create the shape layer and set its path
        let borderLayer = CAShapeLayer()

        borderLayer.frame       = rect
        borderLayer.path        = borderPath.cgPath
        borderLayer.strokeColor = borderColor.cgColor
        borderLayer.fillColor   = UIColor.clear.cgColor
        borderLayer.lineWidth   = borderWidth * UIScreen.main.scale

        //Add this layer to give border.
        self.layer.addSublayer(borderLayer)
    }

}

这是 Kamil Nomtek.com 更新到 Swift 3 的答案,并进行了一些改进(主要是语义/命名和使用类协议)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
protocol RoundedBorderProtocol: class {
    func makeBorder(with radius: CGFloat, borderWidth: CGFloat, borderColor: UIColor)
}

extension UIView: RoundedBorderProtocol {
    func makeBorder(with radius: CGFloat, borderWidth: CGFloat, borderColor: UIColor) {
        let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius))

        // Create the shape layer and set its path
        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = maskPath.cgPath

        // Set the newly created shape layer as the mask for the view's layer
        layer.mask = maskLayer

        //Create path for border
        let borderPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius))

        // Create the shape layer and set its path
        let borderLayer = CAShapeLayer()

        borderLayer.frame = bounds
        borderLayer.path = borderPath.cgPath
        borderLayer.strokeColor = borderColor.cgColor
        borderLayer.fillColor = UIColor.clear.cgColor

        //The border is in the center of the path, so only the inside is visible.
        //Since only half of the line is visible, we need to multiply our width by 2.
        borderLayer.lineWidth = borderWidth *?2

        //Add this layer to display the border
        layer.addSublayer(borderLayer)
    }
}


CRDave 的答案很有效,但有一个缺陷:当多次调用时,如大小更改,它会不断添加层。相反,应该更新以前的图层。
有关更新的 ObjC 版本,请参见下文。为了迅速,请相应地适应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//  UIView+Border.h
#import <UIKit/UIKit.h>

@interface UIView (Border)
- (void)setBorderWithCornerRadius:(CGFloat)radius
                            color:(UIColor *)borderColor
                            width:(CGFloat)borderWidth;
@end

//  UIView+Border.m
#import"UIView+Border.h"

@implementation UIView (Border)
- (void)setBorderWithCornerRadius:(CGFloat)radius
                            color:(UIColor *)borderColor
                            width:(CGFloat)borderWidth {
    CGRect rect = self.bounds;

    //Make round
    // Create the path for to make circle
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect
                                                   byRoundingCorners:UIRectCornerAllCorners
                                                         cornerRadii:CGSizeMake(radius, radius)];

    // Create the shape layer and set its path
    CAShapeLayer *maskLayer = [CAShapeLayer layer];

    maskLayer.frame = rect;
    maskLayer.path  = maskPath.CGPath;

    // Set the newly created shape layer as the mask for the view's layer
    self.layer.mask = maskLayer;

    //Give Border
    //Create path for border
    UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect:rect
                                                     byRoundingCorners:UIRectCornerAllCorners
                                                           cornerRadii:CGSizeMake(radius, radius)];

    // Create the shape layer and set its path
    NSString *layerName = @"ig_border_layer";
    CAShapeLayer *borderLayer = (CAShapeLayer *)[[[self.layer sublayers] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"name == %@", layerName]] firstObject];
    if (!borderLayer) {
        borderLayer = [CAShapeLayer layer];
        [borderLayer setName:layerName];
        //Add this layer to give border.
        [[self layer] addSublayer:borderLayer];
    }

    borderLayer.frame       = rect;
    borderLayer.path        = borderPath.CGPath;
    borderLayer.strokeColor = [UIColor whiteColor].CGColor;
    borderLayer.fillColor   = [UIColor clearColor].CGColor;
    borderLayer.lineWidth   = borderWidth;
}
@end


de.\\ 的回答对我来说比 CRDave\\ 的回答好得多。

我必须从 Swift 翻译它,所以我想我会继续发布翻译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
extension UIView {
    func giveBorderWithCornerRadius(cornerRadius r: CGFloat, borderColor c: UIColor, strokeWidth w: CGFloat) {
        let rect = self.bounds

        let maskPath = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: r, height: r))

        let maskLayer = CAShapeLayer()

        maskLayer.frame = rect
        maskLayer.path = maskPath.cgPath

        self.layer.mask = maskLayer

        let borderPath = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: r, height: r))

        let layerName ="border_layer"
        var borderLayer: CAShapeLayer? = self.layer.sublayers?.filter({ (c) -> Bool in
            if c.name == layerName {
                return true
            } else {
                return false
            }
        }).first as? CAShapeLayer

        if borderLayer == nil {
            borderLayer = CAShapeLayer()
            borderLayer!.name = layerName
            self.layer.addSublayer(borderLayer!)
        }

        borderLayer!.frame = rect
        borderLayer!.path = borderPath.cgPath
        borderLayer!.strokeColor = c.cgColor
        borderLayer!.fillColor = UIColor.clear.cgColor
        borderLayer!.lineWidth = w
    }
}

我正在调用 layoutSubviews()

的方法


删除

1
2
button.layer.borderWidth = 0.3;
button.layer.borderColor = [[UIColor blueMain] CGColor];