-2

I need to have a border and corner radius on the section of my collectionView. So if it's the first row I need to draw a bottomless rect layer. And if it's the last row I will draw a topless layer.

How to achieve my drawing using a bezier path?

enter image description here

Edited

I have used this code, it works. But I could not create a method for topless rect.

extension CGMutablePath {
 static func bottomlessRoundedRect(in rect: CGRect, radius: CGFloat) -> CGMutablePath {
    let path = CGMutablePath()
    path.move(to: CGPoint(x: rect.minX, y: rect.maxY))
    path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.maxX, y: rect.minY), radius: radius)
    path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.minY), tangent2End: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius)
    path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
    return path
 }
}

let layer = CAShapeLayer()
layer.lineWidth = 1
layer.strokeColor = UIColor.black.cgColor
layer.fillColor = nil
layer.path = CGMutablePath.bottomlessRoundedRect(in: testView.bounds.insetBy(dx: 1, dy: 1), radius: 18)
view.layer.insertSublayer(layer, at: 0)

view.layoutIfNeeded()

Here is method for topless rect, it is not working correctly.

static func toplessRoundedRect(in rect: CGRect, radius: CGFloat) -> CGMutablePath {
    let path = CGMutablePath()
    path.move(to: CGPoint(x: rect.minX, y: rect.minY))
    path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.minX, y: rect.maxY), radius: radius)

    path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX, y: rect.minY), radius: radius)
    path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
    return path
}

This is the result. enter image description here

Please help me to have a correct topless rect. (I have not used drawing before)

The answer

static func toplessRoundedRect(in rect: CGRect, radius: CGFloat) -> CGMutablePath {
    let path = CGMutablePath()
    path.move(to: CGPoint(x: rect.minX, y: rect.minY))
    path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius)

    path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX, y: rect.minY), radius: radius)
    path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
    return path
}
developer_I1
  • 65
  • 1
  • 8
  • 1
    What is the question? – matt Feb 01 '21 at 19:51
  • I have edited. I need to draw these topless and bottomless rects with bezierpath – developer_I1 Feb 01 '21 at 19:55
  • 1
    What have you tried already? – koen Feb 01 '21 at 19:57
  • 1
    "I need" is not a question. It's a spec. What's the _programming problem_ you are having responding to that spec? Show us the code etc. – matt Feb 01 '21 at 19:58
  • I have edited . – developer_I1 Feb 01 '21 at 20:11
  • If you were drawing one of these with a pencil, how would you do it? If you were telling someone else how to draw one of these with a pencil, what would you say? – Caleb Feb 01 '21 at 20:13
  • @Caleb thanks for the reply, I searched and found this extension from the internet. I hope it's ok to find code from the internet. I tried to create a topless method but It failed. – developer_I1 Feb 01 '21 at 20:19
  • @developer_I1 The point is, you'd probably tell someone: "Draw a vertical line downward 1 inch, and then a 90° arc with a 1/4" radius to the right, and then a 2" line, another 90° arc upward, and another 1" line." So your code should probably look like that too. Descriptions like *it failed* aren't worth much here; show the code you used and explain what's wrong with it, and you'll get a lot more help. But don't expect someone to just do it for you. – Caleb Feb 01 '21 at 20:29
  • 1
    And when you post code and say "I have this code and it works but I could not create a method for a topless rect" be honest, and state "I found the following code on the web that draws a bottomless rect, and it does what I need, but I don't understand it well enough to create a topless rect version. Here is what I've tried `` but I am having `` – Duncan C Feb 01 '21 at 20:36
  • @DuncanC I said "I have this code" but I have not said "this code has been written by me". You are right I have not understand it well enough, so I posted a question. Thanks for reply. – developer_I1 Feb 01 '21 at 21:00
  • @Caleb, I totally agree with you. Now I edited that post. – developer_I1 Feb 01 '21 at 21:04
  • 2
    When you post code that isn't yours and you're having trouble understanding it/adapting it, it's a good idea to state that explicilty. – Duncan C Feb 01 '21 at 21:28

2 Answers2

3

Diagram it out. Rounded rectangles are a series of line segments connected to quarter-circle arcs of a given radius. (the corner radius.)

iOS uses different coordinate systems depending on how you're doing your drawing (Core Graphics uses LLO, or lower left orgin, and UIKit/Core Animation uses ULO, or upper left origin.)

It looks like the CGPaths used in CAShapeLayers use ULO (upper-left-origin) coordinates, where 0,0 is in the upper left corner and Y increases as you go down.

Your bottomlessRoundedRect() function starts with this line of code:

path.move(to: CGPoint(x: rect.minX, y: rect.maxY))

That moves to the lower left (max Y) position of the rectangle to start.

Annotating the whole function, here's what it does:

static func bottomlessRoundedRect(in rect: CGRect, radius: CGFloat) -> CGMutablePath {
    let path = CGMutablePath()
    //Move to the lower left corner of the rect (starting point)
    path.move(to: CGPoint(x: rect.minX, y: rect.maxY))
    
    //Draw a line from the starting point to the beginning of the arc in the
    //top left corner, and draw the top left rounded corner
    path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY),
                tangent2End: CGPoint(x: rect.maxX, y: rect.minY),
                radius: radius)
    
    //Draw a line from the top left corner to the begnning of the top right
    //arc, and the top right corner arc
    path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.minY),
                tangent2End: CGPoint(x: rect.maxX, y: rect.maxY),
                radius: radius)
    
    //Draw a final line from the end of the top right corner arc to
    //the bottom right corner
    path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
    return path
}

Draw your full rectangle. Pick your corner radius. Draw an inner rectangle who's corners are inset by the corner radius. Draw circles at the corners of your inner rectangle, and note how they meet the sides of your outer rectangles.

Now put it together.

Open a bezier path. Move to your desired starting point. Create a line to the next side, minus your corner radius. Draw a 1/4 circle arc starting and ending at the angles needed to complete that corner. Draw the next line segment. Draw another arc. Draw your final line segment.

An annotated version of a toplessRoundedRect() function looks like this:

static func toplessRoundedRect(in rect: CGRect, radius: CGFloat) -> CGMutablePath {
    let path = CGMutablePath()
    
    //Move to the top left corner.
    path.move(to: CGPoint(x: rect.minX, y: rect.minY))
    
    //Draw a line from the top left corner to the begnning of the bottom left
    //rounded corner, plus the bottom left rounded corner
    path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.maxY),
                tangent2End: CGPoint(x: rect.maxX, y: rect.maxY),
                radius: radius)
    
    //Draw a line from the end of the bottom left rounded corner to the beginning
    //of the bottom right rounded corner, plus the bottom right rounded corner
    path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY),
                tangent2End: CGPoint(x: rect.maxX, y: rect.minY),
                radius: radius)
    
    //Draw a line from the end of the bottom right rounded corner
    //to the top right corner.
    path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
    return path
}
Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • I noticed that the code you posted uses a form of addArc() that I hadn't used before: `addArc(tangent1End:tangent2End:radius:transform:)`. That form is a little easier to use for creating rounded rectangles than the `addArc(center:radius:startAngle:endAngle:clockwise:transform:)` form. – Duncan C Feb 03 '21 at 14:11
2

Here's a tip: work through your code one line at a time.

Change your CGMutablePath extension to this:

extension CGMutablePath {
    static func bottomlessRoundedRect(in rect: CGRect, radius: CGFloat) -> CGMutablePath {
        let path = CGMutablePath()
        // 1
        path.move(to: CGPoint(x: rect.minX, y: rect.maxY))
        // 2
        //path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.maxX, y: rect.minY), radius: radius)
        // 3
        //path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.minY), tangent2End: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius)
        // 4
        //path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        return path
    }
}

When you run your app, only the 1 part of the path will be executed. You won't see anything, because all you've done so far is move to a point.

Now, uncomment the 2 part. Run your code, and see what the result is.

Uncomment the 3 part. Run your code, and see what the result is.

Uncomment the 4 part. Run your code, and see what the result is.

By now, you should have a pretty good idea of what the code is doing, and creating your toplessRoundedRect func should be a piece of cake.

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • I got it. Many thanks! static func toplessRoundedRect(in rect: CGRect, radius: CGFloat) -> CGMutablePath { let path = CGMutablePath() path.move(to: CGPoint(x: rect.minX, y: rect.minY)) path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius) path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX, y: rect.minY), radius: radius) path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY)) return path} – developer_I1 Feb 01 '21 at 22:15