可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
My application is mostly round- and borderbased.
I use the layer property of UIView
to give a corner radius and a border.
But I am facing a problem that the corners are not clear.
I am getting the following results:
UIButton
UIImageView
You can observe a thin border line around the white or grey border.
This is my code:
button.layer.borderWidth = 2.0;
button.layer.borderColor = [[UIColor whiteColor] CGColor];
button.layer.cornerRadius = 4;
button.clipsToBounds = YES;
I have searched to solve this but I'm not getting success.
I have tried button.layer.masksToBounds = YES
, but with no effect.
Am I missing anything? Or are there any other methods which can give me better results compared to CALayer
?
回答1:
I tried many solution and end by using UIBezierPath
.
I create category of UIView
and add method to make round rect and border.
This is method of that category:
- (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];
}
I get idea of using UIBezierPath
from this amazing article: Thinking like a Bézier path
I get most of code from this two link:
- UIView category for rounding just the corners which you want, not all like CALayer cornerRadius.
- How to get a border on UIBezierPath
Note: This is category method so self represent view on which this method is called. Like UIButton, UIImageView etc.
回答2:
Here is a Swift 5 version of @CRDave's answer as an extension of UIView:
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)
}
}
回答3:
This is Kamil Nomtek.com's answer updated to Swift 3+ and with some refinements (mostly semantics/naming and using a class protocol).
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)
}
}
回答4:
The answer by CRDave works great but has one flaw: When called multiple times like on size change, it keeps adding layers. Instead previous layers should be updated.
See below for an updated ObjC version. For swift please adapt accordingly.
// 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
回答5:
de.'s answer worked much better for me than CRDave's answer.
I had to translate it from Swift, so I thought I'd go ahead and post the translation:
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
}
}
I'm calling the method from layoutSubviews()
回答6:
delete
button.layer.borderWidth = 0.3;
button.layer.borderColor = [[UIColor blueMain] CGColor];