Reputation: 640
I create this post to answer to the question on hold posting at Create a UIView with rounded top edge
The question is how to create a rounded UIView like this ?
... and NOT like this (2 rounded corners)
Answer : https://stackoverflow.com/a/28075863/1952147
Upvotes: 1
Views: 1182
Reputation: 640
The solution is combined an oval and a rectangle with bezierPath.
You can add height offset if needed to customize more the curve.
@implementation UIView (RoundedCorners)
-(void)setRoundedRectWithOvalWidthOffset:(CGFloat)offset {
CGRect bounds = self.bounds;
CGRect rectBounds = CGRectMake(bounds.origin.x,
bounds.origin.y + bounds.size.height/2,
bounds.size.width,
bounds.size.height/2);
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:rectBounds];
//[rect addClip];
CGRect ovalBounds = CGRectMake(bounds.origin.x - offset/2,
bounds.origin.y,
bounds.size.width + offset,
bounds.size.height);
UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect:ovalBounds];
//[oval addClip];
[rectPath appendPath:ovalPath];
// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = bounds;
maskLayer.path = rectPath.CGPath;
// Set the newly created shape layer as the mask for the view's layer
self.layer.mask = maskLayer;
}
@end
Code for swift 3.0
//MARK: - UIView Extension
extension UIView {
func setTopCurve(){
let offset = CGFloat(self.frame.size.height/4)
let bounds = self.bounds
let rectBounds = CGRect(x: bounds.origin.x, y: bounds.origin.y + bounds.size.height/2 , width: bounds.size.width, height: bounds.size.height / 2)
let rectPath = UIBezierPath(rect: rectBounds)
let ovalBounds = CGRect(x: bounds.origin.x - offset / 2, y: bounds.origin.y, width: bounds.size.width + offset, height: bounds.size.height)
let ovalPath = UIBezierPath(ovalIn: ovalBounds)
rectPath.append(ovalPath)
let maskLayer = CAShapeLayer.init()
maskLayer.frame = bounds
maskLayer.path = rectPath.cgPath
self.layer.mask = maskLayer
}
}
Upvotes: 4
Reputation: 14296
Using Extension:
extension UIView {
func addTopRoundedCornerToView(targetView:UIView?, desiredCurve:CGFloat?)
{
let offset:CGFloat = targetView!.frame.width/desiredCurve!
let bounds: CGRect = targetView!.bounds
let rectBounds: CGRect = CGRectMake(bounds.origin.x, bounds.origin.y+bounds.size.height / 2, bounds.size.width, bounds.size.height / 2)
let rectPath: UIBezierPath = UIBezierPath(rect: rectBounds)
let ovalBounds: CGRect = CGRectMake(bounds.origin.x - offset / 2, bounds.origin.y, bounds.size.width + offset, bounds.size.height)
let ovalPath: UIBezierPath = UIBezierPath(ovalInRect: ovalBounds)
rectPath.appendPath(ovalPath)
// Create the shape layer and set its path
let maskLayer: CAShapeLayer = CAShapeLayer()
maskLayer.frame = bounds
maskLayer.path = rectPath.CGPath
// Set the newly created shape layer as the mask for the view's layer
targetView!.layer.mask = maskLayer
}
}
Usage:
override func viewWillAppear(animated: Bool) {
self.navigationController?.navigationBarHidden = true
self.view.addTopRoundedCornerToView(self.view, desiredCurve: 0.6)
}
Upvotes: 0
Reputation: 131436
It's actually much simpler than that. There is a method in UIBezierPath,
bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:
, that takes a bit mask that lets you control which corners are rounded and which are not. It will create a path for you that only rounds 2 of 4 corners if that's what you want.
I thought there was a corresponding CGPath function, but I don't see one in the docs. (UIBezierPath uses CGPath under the covers, so most of the time UIBezierPath methods have corresponding CGPath functions. I guess the UIBezierPath is built from primitives like lines and arcs.
If what you need is a CGPath you can always use the UIBezierPath method and then get a CGPath from the resulting UIBezierPath.
It's also possible to build a rounded rectangle yourself by combining the lines for the sides with arcs for the corners, but figuring out how to do it is a bit of a head-scratcher.
EDIT: My solution works, but the result makes the top into a semicircle who's radius is half the rectangle's width, not the flattened oval in the OPs question.
It looks like this:
If the requirement is that the top be a flattened oval then your solution, or a custom path built with a mixture of lines and cubic Bezier curves would be the way to go.
Upvotes: 0
Reputation: 3291
What about this?
// Create the view
UIView *theView = [[UIView alloc] initWithFrame:CGRectMake(50, 220, 200, 200)];
[theView setBackgroundColor:[UIColor orangeColor]];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:theView.bounds byRoundingCorners:UIRectCornerTopRight|UIRectCornerTopLeft cornerRadii:CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = theView.bounds;
maskLayer.path = maskPath.CGPath;
theView.layer.mask = maskLayer;
[self.view addSubview:theView];
Upvotes: 0