3

I would like to create an UIView with rounded top edge like this image, how can I do it please?

Wanted result

Wanted

Not wanted result

Not wanted

Kenzo
  • 640
  • 6
  • 17
  • You couldn't find anything about this already on Stack Overflow? Really? _Really?_ – matt Jan 18 '15 at 18:23
  • I already try this, but it's not give the result as I want: UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)]; – Kenzo Jan 18 '15 at 18:24
  • @matt If you found the answer anywhere else, please show the link so ! – Kenzo Jan 18 '15 at 18:45
  • For example: http://stackoverflow.com/questions/2264083/rounded-uiview-using-calayers-only-some-corners-how – matt Jan 18 '15 at 18:52
  • @matt: thanks for the link, but it's about rounding corners... not the edge – Kenzo Jan 18 '15 at 19:01
  • @matt: Your link just confirm there is not already an answer of my question! Corner is a corner, edge is an edge. – Kenzo Jan 18 '15 at 19:28

6 Answers6

4

Image

This answer really helped me I achieve this effect by this snippet of code:

Swift 3.1

extension UIView {
    func addBottomRoundedEdge(desiredCurve: CGFloat?) {
        let offset: CGFloat = self.frame.width / desiredCurve!
        let bounds: CGRect = self.bounds

        let rectBounds: CGRect = 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 = UIBezierPath(rect: rectBounds)
        let ovalBounds: CGRect = CGRect(x: bounds.origin.x - offset / 2, y: bounds.origin.y, width: bounds.size.width + offset, height: bounds.size.height)
        let ovalPath: UIBezierPath = UIBezierPath(ovalIn: ovalBounds)
        rectPath.append(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
        self.layer.mask = maskLayer
    }
}
kamwysoc
  • 6,709
  • 2
  • 34
  • 48
4

Overide draw method of UIView Class.

For Curved Top Edges

override func draw(_ rect: CGRect) {
        let y:CGFloat = 20
        let curveTo:CGFloat = 0

        let myBezier = UIBezierPath()
        myBezier.move(to: CGPoint(x: 0, y: y))
        myBezier.addQuadCurve(to: CGPoint(x: rect.width, y: y), controlPoint: CGPoint(x: rect.width / 2, y: curveTo))
        myBezier.addLine(to: CGPoint(x: rect.width, y: rect.height))
        myBezier.addLine(to: CGPoint(x: 0, y: rect.height))
        myBezier.close()
        let context = UIGraphicsGetCurrentContext()
        context!.setLineWidth(4.0)
        //UIColor.white.setFill()
        backgroundColor?.setFill()
        myBezier.fill()
        let maskLayer: CAShapeLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = myBezier.cgPath
        self.layer.mask = maskLayer

    }

For Curved Bottom Edges

override func draw(_ rect: CGRect) {

            let y:CGFloat = 15
            let curveTo:CGFloat = 0

            let myBezier = UIBezierPath()
            myBezier.move(to: CGPoint(x: 0, y: rect.height - y ))
            myBezier.addQuadCurve(to: CGPoint(x: rect.width, y:  rect.height - y), controlPoint: CGPoint(x: rect.width / 2, y: rect.height))
            myBezier.addLine(to: CGPoint(x: rect.width, y: 0))
            myBezier.addLine(to: CGPoint(x: 0, y: 0))
            myBezier.close()
            let context = UIGraphicsGetCurrentContext()
            context!.setLineWidth(4.0)
            //UIColor.white.setFill()
            backgroundColor?.setFill()
            myBezier.fill()
            let maskLayer: CAShapeLayer = CAShapeLayer()
            maskLayer.frame = bounds
            maskLayer.path = myBezier.cgPath
            self.layer.mask = maskLayer

        }
1

To repost an answer I posted on a different thread:

I can now confirm that this is a bug introduced after iOS 6. I have an old 4s running iOS 6.1. On that machine, this code:

  path = [UIBezierPath bezierPathWithRoundedRect: bounds
    byRoundingCorners: UIRectCornerTopLeft | UIRectCornerTopRight
   cornerRadii: CGSizeMake(bounds.size.width/2, bounds.size.width/6)
 ];

Creates a rounded rectangle with the corners oval-shaped. The curve is much more gradual on the top part of the curve, and much sharper on the sides, as you would expect:

This is the iOS 6.1 image, with the corners as they should be: This is the iOS 6.1 image, with the corners as they should be

And here is what the same code looks like when run from iOS 8.1.2: enter image description here

It appears that on iOS >=7.0, it ignores the height of the specified radius and uses the width value for both the height and the width of the corner ovals, which forces them to always be quarter circles.

I've logged a bug on apple's bug reporter system. We'll see what they say. I suggest everybody else who's seeing this problem report a bug also. The more reports they get, the more likely they are to fix it.

(ignore the UISwitch at the bottom. That's left over from a previous test.)

Edit: I've written code that builds a bezier curve that roughly approximates the look of the curve generated by iOS < 7. I just found an article on approximating circles with bezier curves. Using the control points from that article it wouldn't be that hard to build our own equivalent of the bezierPathWithRoundedRect:byRoundingCorners:cornerRadii: method that works on all versions of iOS.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
1

The correct way to do this (or one of the correct ways) is to use UIBezierCurve's addQuadCurveToPoint:controlPoint. You can draw a quadratic curve from wherever the path is to a point, using another point as a control point which is a way of determining how much arc and curve the resulting path will have. Don't forget though, if you're using a layer as a mask, you also have to draw the rectangle portion of the shape in as well, or else the mask will just be the curve.

barndog
  • 6,975
  • 8
  • 53
  • 105
0

enter image description here

Once I found this somewhere in SO. It works great. Here is the code:

-(UIView *)roundCornersOnView:(UIView *)view onTopLeft:(BOOL)tl topRight:(BOOL)tr bottomLeft:(BOOL)bl bottomRight:(BOOL)br radius:(float)radius {

    if (tl || tr || bl || br) {
        UIRectCorner corner = (bl ? UIRectCornerBottomLeft : 0) | (br ? UIRectCornerBottomRight : 0) | (tl ? UIRectCornerTopLeft : 0) | (tr ? UIRectCornerTopRight : 0);
        UIView *roundedView = view;
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:roundedView.bounds byRoundingCorners:corner cornerRadii:CGSizeMake(radius, radius)];
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.frame = roundedView.bounds;
        maskLayer.path = maskPath.CGPath;
        roundedView.layer.mask = maskLayer;
        return roundedView;
    } else {
        return view;
    }

}

Let your View name is _tView, then call:

_tView = (UIView *)[self roundCornersOnView:_tView onTopLeft:YES topRight:YES bottomLeft:NO bottomRight:NO radius:25.0];

This will give the output like this:

Rashad
  • 11,057
  • 4
  • 45
  • 73
  • Why not just take in the `UIRectCorner` as a single bitmask field - seems odd expanding the argument list to then just compact it back to the correct form. – Paul.s Jan 18 '15 at 13:21
  • @Rashad: I already found some solutions about rounded corners, but to have result like my photo, the edge must be curved, not the cornes. – Kenzo Jan 18 '15 at 18:17
  • @Kenzo > Increase the width of the view and make corner radius large. It will work. :) – Rashad Jan 19 '15 at 02:27
  • Increase the corner can't not give exactly this shape http://i.stack.imgur.com/DAyOs.jpg – Kenzo Jan 19 '15 at 10:42
0

There's also a simple way to do this in storyboards.

  1. Create a UIView with no background color (clearColor).
  2. Inside this view create two more views with bg color that you need.
  3. Put one view on top of the other, go to the Identity Inspector in the right panel. And add "layer.cornerRadius = desiredRadius" in the User Defined Runtime Attributes section.
  4. You now have two views with bg color. One with rounded corners and one without, just adjust their placement to achieve the look you're after and that's it. No coding required.
Senõr Ganso
  • 1,694
  • 16
  • 23