Iphone 使用CALayers的圆形UIView-只有一些角-如何?
在我的应用程序中,有四个按钮,名称如下: 左上角 左下角 右上角 右下角 按钮上方有一个图像视图或UIView 现在,假设用户点击左上角的按钮。上面的图像/视图应在该特定角处四舍五入 我在将圆角应用于UIView时遇到一些困难 现在,我使用以下代码将圆角应用于每个视图:Iphone 使用CALayers的圆形UIView-只有一些角-如何?,iphone,objective-c,xcode,uiview,calayer,Iphone,Objective C,Xcode,Uiview,Calayer,在我的应用程序中,有四个按钮,名称如下: 左上角 左下角 右上角 右下角 按钮上方有一个图像视图或UIView 现在,假设用户点击左上角的按钮。上面的图像/视图应在该特定角处四舍五入 我在将圆角应用于UIView时遇到一些困难 现在,我使用以下代码将圆角应用于每个视图: // imgVUserImg is a image view on IB. imgVUserImg.image=[UIImage imageWithData:[NSData dataWithContentsOfU
// imgVUserImg is a image view on IB.
imgVUserImg.image=[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"any Url Here"];
CALayer *l = [imgVUserImg layer];
[l setMasksToBounds:YES];
[l setCornerRadius:5.0];
[l setBorderWidth:2.0];
[l setBorderColor:[[UIColor darkGrayColor] CGColor]];
上述代码将圆度应用于所提供视图的每个角。相反,我只想将圆度应用于选定的角点,如-top/top+left/bottom+right等
可能吗?如何操作?请参阅。您必须将自己的矩形绘制到带有一些圆角的CGPath,将CGPath添加到CGContext,然后使用CGContextClip对其进行剪裁
您还可以将带有alpha值的圆角矩形绘制到图像中,然后使用该图像创建一个新层,并将其设置为图层的遮罩属性。我使用at的答案和中的代码生成此代码
然后我意识到我回答了错误的问题,给出了一个完整的UILabel而不是UIImage,因此我使用以下代码来更改它:
使用视图模板创建iPhone项目。在视图控制器中,添加以下内容:
- (void)viewDidLoad
{
CGRect rect = CGRectMake(10, 10, 200, 100);
MyView *myView = [[MyView alloc] initWithFrame:rect];
[self.view addSubview:myView];
[super viewDidLoad];
}
MyView只是一个UIImageView子类:
@interface MyView : UIImageView
{
}
我以前从未使用过图形上下文,但我成功地将这些代码拼凑在一起。其中两个角的代码丢失了。如果您阅读代码,您可以看到我是如何通过删除一些CGContextAddArc调用和代码中的一些radius值来实现这一点的。所有角点的代码都在那里,因此将其作为起点,删除创建不需要的角点的部分。请注意,如果需要,您也可以制作具有2或3个圆角的矩形
代码并不完美,但我相信你可以稍微整理一下
static void addRoundedRectToPath(CGContextRef context, CGRect rect, float radius, int roundedCornerPosition)
{
// all corners rounded
// CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
// CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
// CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius,
// radius, M_PI / 4, M_PI / 2, 1);
// CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius,
// rect.origin.y + rect.size.height);
// CGContextAddArc(context, rect.origin.x + rect.size.width - radius,
// rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
// CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + radius);
// CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + radius,
// radius, 0.0f, -M_PI / 2, 1);
// CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
// CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius,
// -M_PI / 2, M_PI, 1);
// top left
if (roundedCornerPosition == 1) {
CGContextMoveToPoint(context, rect.origin.x, rect.origin.y + radius);
CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height - radius);
CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius,
radius, M_PI / 4, M_PI / 2, 1);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y);
CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y);
}
// bottom left
if (roundedCornerPosition == 2) {
CGContextMoveToPoint(context, rect.origin.x, rect.origin.y);
CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y);
CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y);
CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + radius, radius,
-M_PI / 2, M_PI, 1);
}
// add the other corners here
CGContextClosePath(context);
CGContextRestoreGState(context);
}
-(UIImage *)setImage
{
UIImage *img = [UIImage imageNamed:@"my_image.png"];
int w = img.size.width;
int h = img.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextBeginPath(context);
CGRect rect = CGRectMake(0, 0, w, h);
addRoundedRectToPath(context, rect, 50, 1);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawImage(context, rect, img.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
[img release];
return [UIImage imageWithCGImage:imageMasked];
}
不要忘了,您需要在其中安装QuartzCore框架才能正常工作。从iOS 3.2开始,您可以使用UIBezierPath的功能创建一个开箱即用的圆角矩形,其中只有您指定的角是圆角的。然后,您可以将其用作CAShapeLayer的路径,并将其用作视图层的遮罩:
// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds
byRoundingCorners:UIRectCornerTopLeft
cornerRadii:CGSizeMake(10.0, 10.0)];
// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = imageView.bounds;
maskLayer.path = maskPath.CGPath;
// Set the newly created shape layer as the mask for the image view's layer
imageView.layer.mask = maskLayer;
就这样-不要在核心图形中手动定义形状,也不要在Photoshop中创建遮罩图像。该层甚至不需要失效。应用圆角或更改为新的角点与定义新的UIBezierPath并将其CGPath用作遮罩层的路径一样简单。bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:方法的corners参数是一个位掩码,因此可以通过将多个角组合在一起进行圆角。
编辑:添加阴影
如果你想给它添加一个阴影,需要做更多的工作
由于imageView.layer.mask=maskLayer应用遮罩,因此通常不会在其外部显示阴影。诀窍是使用透明视图,然后将两个子层Calayer添加到视图的层:shadowLayer和roundedLayer。两者都需要使用UIBezierPath。图像将作为roundedLayer的内容添加
我在我的代码中的很多地方都使用过这段代码,它100%正确。您可以通过更改一个属性byRoundingCorners:UIRectCornerBottomLeft来更改任何录像机
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerBottomLeft cornerRadii:CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = view.bounds;
maskLayer.path = maskPath.CGPath;
view.layer.mask = maskLayer;
[maskLayer release];
有一个更简单、更快的答案,可以根据您的需要使用,也可以使用阴影。可以将超层上的maskToBounds设置为true,并偏移子层,使其两个角位于超层边界之外,从而有效地将两个边上的圆角切掉
当然,只有当您希望在同一侧只有两个圆角,并且从一侧剪切几个像素时,层的内容看起来是相同的时,这才有效。使条形图只在顶部四舍五入非常有用。Stuarts将特定角四舍五入的示例非常有用。如果你想绕多个角,比如左上角和右上角,这就是方法
// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageview
byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight
cornerRadii:CGSizeMake(10.0, 10.0)];
// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = imageview.bounds;
maskLayer.path = maskPath.CGPath;
// Set the newly created shape layer as the mask for the image view's layer
imageview.layer.mask = maskLayer;
只舍入一些角不会很好地进行自动调整大小或自动布局
因此,另一种选择是使用常规的拐角半径,并将不需要的拐角隐藏在另一个视图下或其superview边界之外,确保将其设置为剪辑其内容。总结Stuart的答案,可以使用如下圆角方法:
@implementation UIView (RoundCorners)
- (void)applyRoundCorners:(UIRectCorner)corners radius:(CGFloat)radius {
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
@end
因此,要应用圆角,只需执行以下操作:
[self.imageView applyRoundCorners:UIRectCornerTopRight|UIRectCornerTopLeft radius:10];
虽然已经晚了五年,但我认为目前人们这样做的方式并非100%正确。很多人都有过这样的问题:使用UIBezierPath+CAShapeLayer方法会干扰自动布局,特别是当它设置在情节提要上时。没有答案,所以我决定加上我自己的 有一种非常简单的方法可以避免这种情况:在drawRect:CGRect函数中绘制圆角 举个例子,如果我想上一轮 对于UIView,我将子类UIView,然后在适当的地方使用该子类
import UIKit
class TopRoundedView: UIView {
override func drawRect(rect: CGRect) {
super.drawRect(rect)
var maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: UIRectCorner.TopLeft | UIRectCorner.TopRight, cornerRadii: CGSizeMake(5.0, 5.0))
var maskLayer = CAShapeLayer()
maskLayer.frame = self.bounds
maskLayer.path = maskPath.CGPath
self.layer.mask = maskLayer
}
}
这是解决问题的最佳方法,不需要花任何时间去适应。谢谢分享。在这里,我想分享swift 2.0的解决方案,以供进一步参考。要符合UIRectCorner的协议
let mp = UIBezierPath(roundedRect: cell.bounds, byRoundingCorners: [.bottomLeft, .TopLeft], cornerRadii: CGSize(width: 10, height: 10))
let ml = CAShapeLayer()
ml.frame = self.bounds
ml.path = mp.CGPath
self.layer.mask = ml
带Swift 3+语法的CALayer扩展
它可以像这样使用:
let layer: CALayer = yourView.layer
layer.round(roundedRect: yourView.bounds, byRoundingCorners: [.bottomLeft, .topLeft], cornerRadii: CGSize(width: 5, height: 5))
为了添加到和,我在Swift中创建了一个简单的、可重用的UIView。根据您的用例,您可能希望进行修改,以避免在每个布局上创建对象等,但我希望使其尽可能简单。如果您不喜欢子类化,扩展允许您将此应用于其他视图的ex.UIImageView
extension UIView {
func roundCorners(_ roundedCorners: UIRectCorner, toRadius radius: CGFloat) {
roundCorners(roundedCorners, toRadii: CGSize(width: radius, height: radius))
}
func roundCorners(_ roundedCorners: UIRectCorner, toRadii cornerRadii: CGSize) {
let maskBezierPath = UIBezierPath(
roundedRect: bounds,
byRoundingCorners: roundedCorners,
cornerRadii: cornerRadii)
let maskShapeLayer = CAShapeLayer()
maskShapeLayer.frame = bounds
maskShapeLayer.path = maskBezierPath.cgPath
layer.mask = maskShapeLayer
}
}
class RoundedCornerView: UIView {
var roundedCorners: UIRectCorner = UIRectCorner.allCorners
var roundedCornerRadii: CGSize = CGSize(width: 10.0, height: 10.0)
override func layoutSubviews() {
super.layoutSubviews()
roundCorners(roundedCorners, toRadii: roundedCornerRadii)
}
}
以下是如何将其应用于UIViewController:
在iOS 11中,我们现在只能绕过一些拐角
let view = UIView()
view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
我建议定义图层的遮罩。遮罩本身应该是具有专用路径的CAShapeLayer对象。您可以使用下一个UIView扩展Swift 4.2:
extension UIView {
func round(corners: UIRectCorner, with radius: CGFloat) {
let maskLayer = CAShapeLayer()
maskLayer.frame = bounds
maskLayer.path = UIBezierPath(
roundedRect: bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius)
).cgPath
layer.mask = maskLayer
}
}
很好的一个,这在分组UITableView单元格的selectedBackgroundViews上帮了我很多忙:-不管怎样,在转弯后要给这个视图添加阴影吗?尝试以下操作但未成功。`self.layer.masksToBounds=否;self.layer.shadowColor=[UIColor blackColor].CGColor;self.layer.shadowRadius=3;self.layer.shadowOffset=CGSizeMake-3,3;self.layer.shadowOpacity=0.8;self.layer.shouldRasterize=是`@克丽斯瓦格纳:关于向圆形视图应用阴影,请参见我的编辑。@StuDev好极了!我已经为阴影视图选择了一个子视图,但这要好多了!没想到会这样添加子层。谢谢为什么我的图像会变成白色,那么当我滚动UITableView并重新创建单元格时,它会工作!为什么?在UITableView子视图(例如UITableViewCells或UITableViewHeaderFooterView)上使用此解决方案时,滚动时会导致平滑度差。另一种使用方法是性能更好的解决方案,它为所有角添加了一个角半径。为了只“显示”特定的圆角,如右上角和右下角,我在frame.origin.x上添加了一个子视图,并为其图层指定了拐角半径。如果有人找到了更好的解决方案,我很感兴趣。在UITableView子视图(例如UITableViewCells或UITableViewHeaderFooterView)上使用此解决方案时,滚动时会导致平滑度差。另一种使用方法是性能更好的解决方案,它为所有角添加了一个角半径。为了只“显示”特定的圆角,如右上角和右下角,我在frame.origin.x上添加了一个子视图,并为其图层指定了拐角半径。如果有人找到了更好的解决方案,我很感兴趣。对于大小可能会改变的视图,它不能像预期的那样工作。示例:UILabel。设置掩码路径,然后通过设置文本来更改大小,将保留旧掩码。将需要子类化和重载drawRect。这不是解决问题的正确方法。只有在视图中进行自定义绘图(例如使用核心图形)时,才应覆盖drawRect_u3;,否则即使是空的实现也会影响性能。每次创建一个新的遮罩层也是不必要的。实际需要做的只是在视图边界更改时更新遮罩层的“路径”特性,而执行此操作的位置在layoutSubviews方法的覆盖范围内。
class MyViewController: UIViewController {
private var _view: RoundedCornerView {
return view as! RoundedCornerView
}
override func loadView() {
view = RoundedCornerView()
}
override func viewDidLoad() {
super.viewDidLoad()
_view.roundedCorners = [.topLeft, .topRight]
_view.roundedCornerRadii = CGSize(width: 10.0, height: 10.0)
}
}
let view = UIView()
view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
extension UIView {
func round(corners: UIRectCorner, with radius: CGFloat) {
let maskLayer = CAShapeLayer()
maskLayer.frame = bounds
maskLayer.path = UIBezierPath(
roundedRect: bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius)
).cgPath
layer.mask = maskLayer
}
}