cornerRadius与border:边框周围的故障

12

我的应用程序大多是基于圆形和边框的。

我使用UIView 的layer属性来添加圆角和边框。

但是我遇到了一个问题,就是圆角不够清晰。

我得到了以下结果:

UIButton

带有圆角和边框的按钮截图

UIImageView

带有圆角和边框的图像视图截图

你可以观察到白色或灰色边框周围有一条细边框线。

这是我的代码:

button.layer.borderWidth = 2.0;
button.layer.borderColor = [[UIColor whiteColor] CGColor];
button.layer.cornerRadius = 4;

button.clipsToBounds = YES;

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

我搜索了解决方法,但没有成功。是否我漏掉了什么?或者是否有其他方法可以比CALayer更好地实现我的目的?


1
当borderWidth也是整数时,它是否会出现故障? - Dávid Kaszás
为测试,我尝试了边框宽度1.5、2.0、2.5和3.0,但在所有情况下都遇到了相同的故障。 - CRDave
6个回答

16
我尝试了许多解决方案,最终使用了UIBezierPath
我创建了一个UIView的类别,并添加了一个方法来制作圆角矩形和边框。
以下是该类别的方法:
- (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的想法:像Bézier路径一样思考
我从以下两个链接中获取了大部分代码: 注意: 这是一个类别方法,因此self代表调用此方法的视图。例如UIButton,UIImageView等。

这是一个非常棒的解决方案。 当我使用 layer.cornerRadius 圆角化时,遇到了奇怪的细暗边框问题,但是通过使用您发布的代码片段,我解决了它。谢谢! - Valerii Lider
1
你忘记使用 borderColor 参数了 ;) - Kamil Nomtek.com
@KamilNomtek.com 的 borderLayer.strokeColor = [UIColor whiteColor].CGColor; 是用于设置边框颜色的。 - CRDave

11

这是一个基于UIView的Swift 5版本扩展,由@CRDave提供答案。

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)
    }

}

7

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

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)
    }
}

3
CRDave的答案非常好,但有一个缺陷:当多次调用时,例如在大小更改时,它会不断添加图层。相反,应该更新以前的图层。
请参见下面的更新ObjC版本。对于Swift,请相应地进行调整。
//  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

是的!谢谢。我将发布一个Swift版本的答案。 - Zane Helton

0

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

我不得不将它从Swift翻译过来,所以我想我会把翻译发出来:

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()方法中调用该方法


-4

删除

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

那条黑色细线不是我设置的边框。请查看编辑后的代码。我给第二张图片白色粗边框,第一张图片灰色边框。但是我得到了额外的厚边框,这是我的问题。 - CRDave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接