UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Creating custom shapes with CoreAnimation/CoreGraphics

Forums > iOS

I'm trying to create a component like:

ticket shape

To do this in code, the only way i know how is to do a CGPath to create this shape.

struct TicketShape {
    let rect: CGRect
    let cornerRadius: CGFloat
    let notchRadius: CGFloat
    let notchOffsetY: CGFloat

    init(rect: CGRect, cornerRadius: CGFloat = 8, notchRadius: CGFloat = 12, notchOffsetY: CGFloat = 0) {
        self.rect = rect
        self.cornerRadius = cornerRadius
        self.notchRadius = notchRadius
        self.notchOffsetY = notchOffsetY
    }

    func makePath() -> CGPath {
        let path = CGMutablePath()
        let notchCenterY = rect.midY + notchOffsetY

        path.move(to: .init(x: cornerRadius, y: 0))

        // from left to right line
        path.addLine(to: .init(x: rect.width - cornerRadius, y: 0))

        // top - right corner
        path.addQuadCurve(
            to: .init(x: rect.width, y: cornerRadius),
            control: .init(x: rect.width, y: 0)
        )

        // top to bottom - right side
        path.addLine(to: .init(x: rect.width, y: notchCenterY - notchRadius))

        // trailing notch
        path.addArc(
            center: .init(x: rect.width, y: notchCenterY),
            radius: notchRadius,
            startAngle: -CGFloat.pi / 2,
            endAngle: CGFloat.pi / 2,
            clockwise: true
        )

        path.addLine(to: .init(x: rect.width, y: rect.height - cornerRadius))

        // bottom right corner
        path.addQuadCurve(
            to: .init(x: rect.width - cornerRadius, y: rect.height),
            control: .init(x: rect.width, y: rect.height)
        )
        path.addLine(to: .init(x: cornerRadius, y: rect.height))

        // bottom left corner
        path.addQuadCurve(
            to: .init(x: 0, y: rect.height - cornerRadius),
            control: .init(x: 0, y: rect.height)
        )
        path.addLine(to: .init(x: 0, y: notchCenterY + notchRadius))

        // leading notch
        path.addArc(
            center: .init(x: 0, y: notchCenterY),
            radius: notchRadius,
            startAngle: CGFloat.pi / 2,
            endAngle: -CGFloat.pi / 2,
            clockwise: true
        )

        path.addLine(to: .init(x: 0, y: cornerRadius))
        path.addQuadCurve(
            to: .init(x: cornerRadius, y: 0),
            control: .init(x: 0, y: 0)
        )

        path.closeSubpath()

        return path
    }

    func makeCAShape() -> CAShapeLayer {
        let shape = CAShapeLayer()
        shape.frame = rect
        shape.path = makePath()
        return shape
    }
}

This code is fairly complicated to understand to achieve such a simple concept. This approach is boils down to draw every line that composes the shape

I was wonderting if there was a different way to achieve this for UIKit.

I wondered if there was a way to "combine shapes" like you do in sketch. Where you create shapes by substracting 2 shapes or more.

In this case to create the ticket shape I would need to:

  1. create a rounded rectangle (A)
  2. create a small circle (B)
  3. remove B from A = C
  4. create a second circle (D)
  5. remove D from C => ticket shape.

As far as i know I cannot just combine layers/shapes in this way.

The other issue is to add this to my UIView class. For that I would do something like:

    override func layoutSubviews() {
        super.layoutSubviews()

        shapeLayer?.fillColor = UIColor.backgroundSecondary.cgColor

        guard shapeLayer == nil else { return }

        let ticketShape = TicketShape(rect: bounds).makeCAShape()
        ticketShape.shadowColor = UIColor.black.cgColor
        ticketShape.shadowRadius = 2
        ticketShape.shadowOffset = CGSize(width: 1, height: 1)
        ticketShape.shadowOpacity = 0.3
        ticketShape.fillColor = UIColor.backgroundSecondary.cgColor

        backgroundView.layer.addSublayer(ticketShape)
        self.shapeLayer = ticketShape
    }

To ensure the color of my shape and the size of it matches the size of the UIView.

I'm basically asking if there are better approaches to do this

3      

Hope you get an answer to your question! I am looking to duplicate the effect of the Blobs in Google's "Blob Opera" have you seen this? It's awesome the way they stretch and wiggle. Love the eye effect and mouth effects.

Looking for a Swift graphics boffin for advice on how to approach this.

I hope you get your answer!

Blob Opera (Google)

3      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.