可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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).
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.
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?
回答1:
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.
Then I realized I\'d answered the wrong question (gave a rounded UILabel instead of UIImage) so I used this code to change it:
http://discussions.apple.com/thread.jspa?threadID=1683876
Make an iPhone project with the View template. In the view controller, add this:
- (void)viewDidLoad
{
CGRect rect = CGRectMake(10, 10, 200, 100);
MyView *myView = [[MyView alloc] initWithFrame:rect];
[self.view addSubview:myView];
[super viewDidLoad];
}
MyView
is just a UIImageView
subclass:
@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 CGContextAddArc
calls, 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.
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
Don\'t forget that you\'ll need to get the QuartzCore framework in there for this to work.
回答2:
Starting in iOS 3.2, you can use the functionality of UIBezierPath
s 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:
// 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 UIBezierPath
and using its CGPath
as the mask layer\'s path. The corners
parameter of the bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:
method is a bitmask, and so multiple corners can be rounded by ORing them together.
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 (CALayer
s) to the view\'s layer: shadowLayer
and roundedLayer
. Both need to make use of the UIBezierPath
. The image is added as the content of 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];
回答3:
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\"
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];
回答4:
In iOS 11, we can now round some corners only
let view = UIView()
view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
回答5:
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
// 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;
回答6:
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)
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
回答7:
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.
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.
回答8:
CALayer extension with Swift 3 and above syntax
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))
回答9:
See this related question. You\'ll have to draw your own rectangle to a CGPath
with some rounded corners, add the CGPath
to your CGContext
and then clip to it using 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 mask
property (see Apple\'s documentation).
回答10:
Half a decade late, but I think the current way people do this isn\'t 100% right. Many people have had the issue that using the UIBezierPath + CAShapeLayer method interferes with Auto-layout, especially when it is set on the Storyboard. No answers go over this, so I decided to add my own.
There is a very easy way to circumvent that: Draw the rounded corners in the drawRect(rect: CGRect)
function.
For example, if I wanted top rounded corners for a UIView, I\'d subclass UIView and then use that subclass wherever appropriate.
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
}
}
This is the best way to conquer the issue and doesn\'t take any time at all to adapt to.
回答11:
Rounding only some corners won\'t play nice with auto resizing or auto layout.
So another option is to use regular cornerRadius
and hide the corners you don\'t want under another view or outside its superview bounds making sure it is set to clip its contents.
回答12:
To add to to the answer and the addition, I created a simple, reusable UIView
in Swift. Depending on your use case, you might want to make modifications (avoid creating objects on every layout etc.), but I wanted to keep it as simple as possible. The extension allows you to apply this to other view\'s (ex. UIImageView
) easier if you do not like subclassing.
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)
}
}
Here\'s how you would apply it to a UIViewController
:
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)
}
}
回答13:
Wrapping up Stuart\'s answer, you can have rounding corner method as the following:
@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
So to apply rounding corner, you simply do:
[self.imageView applyRoundCorners:UIRectCornerTopRight|UIRectCornerTopLeft radius:10];
回答14:
I\'d suggest defining a layer\'s mask. The mask itself should be a CAShapeLayer
object with a dedicated path. You can use the next UIView extension (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
}
}