objective-c 使用 CALayer 的圆形 UIView - 只有一些角落 - 如何?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2264083/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rounded UIView using CALayers - only some corners - How?
提问by Sagar R. Kothari
In my application - there are four buttons named as follows:
在我的应用程序中 - 有四个按钮命名如下:
- Top - left
- Bottom - left
- Top - right
- Bottom - right
- 左上方
- 左下方
- 右上
- 右下角
Above the buttons there is an image view (or a UIView).
在按钮上方有一个图像视图(或 UIView)。
Now, suppose a user taps on - top - left button. Above image / view should be rounded at that particular corner.
现在,假设用户点击左上角的按钮。上面的图像/视图应该在那个特定的角落被四舍五入。
I am having some difficulty in applying rounded corners to the UIView.
我在将圆角应用于 UIView 时遇到了一些困难。
Right now I am using the following code to apply the rounded corners to each view:
现在我正在使用以下代码将圆角应用于每个视图:
// 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]];
Above code is applying the roundness to each of corners of supplied View. Instead I just wanted to apply roundness to selected corners like - top / top+left / bottom+right etc.
上面的代码将圆度应用于提供的视图的每个角。相反,我只想将圆度应用于选定的角,例如 - 顶部 / 顶部 + 左侧 / 底部 + 右侧等。
Is it possible? How?
是否可以?如何?
采纳答案by nevan king
I used the answer over at How do I create a round cornered UILabel on the iPhone?and the code from How is a rounded rect view with transparency done on iphone?to make this code.
我在如何在 iPhone 上创建圆角 UILabel 中使用了答案?以及如何在 iphone 上完成具有透明度的圆形矩形视图中的代码?制作此代码。
Then I realized I'd answered the wrong question (gave a rounded UILabel instead of UIImage) so I used this code to change it:
然后我意识到我回答了错误的问题(给出了一个圆形的 UILabel 而不是 UIImage)所以我用这个代码来改变它:
http://discussions.apple.com/thread.jspa?threadID=1683876
http://discussions.apple.com/thread.jspa?threadID=1683876
Make an iPhone project with the View template. In the view controller, add this:
使用视图模板制作 iPhone 项目。在视图控制器中,添加以下内容:
- (void)viewDidLoad
{
CGRect rect = CGRectMake(10, 10, 200, 100);
MyView *myView = [[MyView alloc] initWithFrame:rect];
[self.view addSubview:myView];
[super viewDidLoad];
}
MyViewis just a UIImageViewsubclass:
MyView只是一个UIImageView子类:
@interface MyView : UIImageView
{
}
I'd never used graphics contexts before, but I managed to hobble together this code. It's missing the code for two of the corners. If you read the code, you can see how I implemented this (by deleting some of the CGContextAddArccalls, and deleting some of the radius values in the code. The code for all corners is there, so use that as a starting point and delete the parts that create corners you don't need. Note that you can make rectangles with 2 or 3 rounded corners too if you want.
我以前从未使用过图形上下文,但我设法将这段代码拼凑在一起。它缺少两个角的代码。如果你阅读代码,你会看到我是如何实现的(通过删除一些CGContextAddArc调用,并删除代码中的一些半径值。所有角落的代码都在那里,所以以此为起点并删除创建不需要的角的部分。请注意,如果需要,您也可以制作带有 2 或 3 个圆角的矩形。
The code's not perfect, but I'm sure you can tidy it up a little bit.
代码并不完美,但我相信你可以稍微整理一下。
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];
}
alt text http://nevan.net/skitch/skitched-20100224-092237.png
替代文字 http://nevan.net/skitch/skitched-20100224-092237.png
Don't forget that you'll need to get the QuartzCore framework in there for this to work.
不要忘记,您需要在其中安装 QuartzCore 框架才能使其工作。
回答by Stuart
Starting in iOS 3.2, you can use the functionality of UIBezierPaths to create an out-of-the-box rounded rect (where only corners you specify are rounded). You can then use this as the path of a CAShapeLayer, and use this as a mask for your view's layer:
从 iOS 3.2 开始,您可以使用UIBezierPaths的功能来创建开箱即用的圆角矩形(只有您指定的角是圆角的)。然后,您可以将其用作 a 的路径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;
And that's it - no messing around manually defining shapes in Core Graphics, no creating masking images in Photoshop. The layer doesn't even need invalidating. Applying the rounded corner or changing to a new corner is as simple as defining a new UIBezierPathand using its CGPathas the mask layer's path. The cornersparameter of the bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:method is a bitmask, and so multiple corners can be rounded by ORing them together.
就是这样 - 无需在 Core Graphics 中手动定义形状,无需在 Photoshop 中创建遮罩图像。该层甚至不需要失效。应用圆角或更改为新角就像定义一个新角UIBezierPath并将其CGPath用作蒙版层的路径一样简单。该方法的corners参数bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:是一个位掩码,因此可以通过将它们组合在一起来使多个角变圆。
EDIT: Adding a shadow
编辑:添加阴影
If you're looking to add a shadow to this, a little more work is required.
如果您想为此添加阴影,则需要做更多的工作。
Because "imageView.layer.mask = maskLayer" applies a mask, a shadow will not ordinarily show outside of it. The trick is to use a transparent view, and then add two sublayers (CALayers) to the view's layer: shadowLayerand roundedLayer. Both need to make use of the UIBezierPath. The image is added as the content of roundedLayer.
因为“ imageView.layer.mask = maskLayer”应用了遮罩,所以通常不会在遮罩外显示阴影。诀窍是使用透明视图,然后将两个子图层(CALayers)添加到视图的图层:shadowLayer和roundedLayer。两者都需要使用UIBezierPath. 图像被添加为 的内容roundedLayer。
// Create a transparent view
UIView *theView = [[UIView alloc] initWithFrame:theFrame];
[theView setBackgroundColor:[UIColor clearColor]];
// Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:theView.bounds
byRoundingCorners:UIRectCornerTopLeft
cornerRadii:CGSizeMake(10.0f, 10.0f)];
// Create the shadow layer
CAShapeLayer *shadowLayer = [CAShapeLayer layer];
[shadowLayer setFrame:theView.bounds];
[shadowLayer setMasksToBounds:NO];
[shadowLayer setShadowPath:maskPath.CGPath];
// ...
// Set the shadowColor, shadowOffset, shadowOpacity & shadowRadius as required
// ...
// Create the rounded layer, and mask it using the rounded mask layer
CALayer *roundedLayer = [CALayer layer];
[roundedLayer setFrame:theView.bounds];
[roundedLayer setContents:(id)theImage.CGImage];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
[maskLayer setFrame:theView.bounds];
[maskLayer setPath:maskPath.CGPath];
roundedLayer.mask = maskLayer;
// Add these two layers as sublayers to the view
[theView.layer addSublayer:shadowLayer];
[theView.layer addSublayer:roundedLayer];
回答by itsaboutcode
I have used this code in many places in my code and it works 100% correctly. You can change any corder by changed one property "byRoundingCorners:UIRectCornerBottomLeft"
我在我的代码中的很多地方都使用了这段代码,它 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];
回答by onmyway133
In iOS 11, we can now round some corners only
在 iOS 11 中,我们现在只能圆一些角
let view = UIView()
view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
回答by Maverick
CALayer extension with Swift 3+syntax
带有Swift 3+语法的CALayer 扩展
extension CALayer {
func round(roundedRect rect: CGRect, byRoundingCorners corners: UIRectCorner, cornerRadii: CGSize) -> Void {
let bp = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: cornerRadii)
let sl = CAShapeLayer()
sl.frame = self.bounds
sl.path = bp.cgPath
self.mask = sl
}
}
It can be used like:
它可以像这样使用:
let layer: CALayer = yourView.layer
layer.round(roundedRect: yourView.bounds, byRoundingCorners: [.bottomLeft, .topLeft], cornerRadii: CGSize(width: 5, height: 5))
回答by Jog Dan
Thanks for sharing. Here I'd like to share the solution on swift 2.0 for further reference on this issue. (to conform the UIRectCorner's protocol)
感谢分享。在这里我想分享一下 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
回答by aZtraL-EnForceR
Stuarts example for rounding specific corners works great. If you want to round multiple corners like top left and right this is how to do it
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;
回答by user1259710
there is an easier and faster answer that may work depending on your needs and also works with shadows. you can set maskToBounds on the superlayer to true, and offset the child layers so that 2 of their corners are outside the superlayer bounds, effectively cutting the rounded corners on 2 sides away.
有一个更简单、更快的答案,可以根据您的需要工作,也适用于阴影。您可以将超层上的 maskToBounds 设置为 true,并偏移子层,使其 2 个角位于超层边界之外,从而有效地将 2 边的圆角切掉。
of course this only works when you want to have only 2 rounded corners on the same side and the content of the layer looks the same when you cut off a few pixels from one side. works great for having bar charts rounded only on the top side.
当然,这只适用于您希望在同一侧只有 2 个圆角并且当您从一侧切掉几个像素时图层的内容看起来相同的情况。非常适合将条形图仅在顶部四舍五入。
回答by MrMage
See this related question. You'll have to draw your own rectangle to a CGPathwith somerounded corners, add the CGPathto your CGContextand then clip to it using CGContextClip.
请参阅此相关问题。您必须将自己的矩形绘制为CGPath带有一些圆角的矩形,将其添加CGPath到您的CGContext,然后使用CGContextClip.
You can also draw the rounded rect with alpha values to an image and then use that image to create a new layer which you set as your layer's maskproperty (see Apple's documentation).
您还可以将带有 alpha 值的圆形矩形绘制到图像上,然后使用该图像创建一个新层,您将其设置为层的mask属性(请参阅 Apple 的文档)。
回答by Rivera
Rounding only some corners won't play nice with auto resizing or auto layout.
在自动调整大小或自动布局时,仅对一些角进行圆角处理效果不佳。
So another option is to use regular cornerRadiusand hide the corners you don't want under another view or outside its superview bounds making sure it is set to clip its contents.
因此,另一种选择是使用常规cornerRadius并将您不想要的角隐藏在另一个视图下或其超视图边界之外,确保将其设置为剪辑其内容。

