user4806509
user4806509

Reputation: 3011

Drawing a 3D cone shape in Swift using a UIBezierPath() and CAShapeLayer()

Background

I’m attempting to draw a 3D cone shape as shown in the image below.

I have a method that I use to draw a simple triangle and fill it with a solid color in Swift using a UIBezierPath() and CAShapeLayer().


Question

In Swift code, how can I draw a 3D cone shape or fill the triangle shape I’ve drawn with a complex gradient that gives the triangle shape a 3D effect and effectively a cone appearance?


Code

import UIKit
class ViewController: UIViewController {
    @IBOutlet weak var myView: UIView!

    override func viewDidLoad() {
        let path = UIBezierPath()

        path.moveToPoint(CGPoint(x: 0, y: 100))
        path.addLineToPoint(CGPoint(x: 200, y: 100))
        path.addLineToPoint(CGPoint(x: 100, y: 0))
        path.closePath()

        let shape = CAShapeLayer()
        shape.path = path.CGPath
        shape.fillColor = UIColor.grayColor().CGColor

        myView.layer.insertSublayer(shape, atIndex: 0)
    }
}

Image

enter image description here

Upvotes: 0

Views: 1012

Answers (1)

HangarRash
HangarRash

Reputation: 15052

I was able to achieve your goal making use of CGGradientLayer with a type of conic. The trick is to use the triangle shape as a mask for the gradient.

Here is an example that can be run in a Swift Playground. Comments in the code.

import UIKit
import PlaygroundSupport

class ConeView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)

        // Just for display
        backgroundColor = .yellow

        // Create the triangle to be used as the gradient's mask
        // Base the size of the triangle on the view's frame
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 0, y: frame.height))
        path.addLine(to: CGPoint(x: frame.width, y: frame.height))
        path.addLine(to: CGPoint(x: frame.width / 2, y: 0))
        path.close()

        // Create a shape from the path
        let shape = CAShapeLayer()
        shape.path = path.cgPath

        // Create a conical gradient and mask it with the triangle shape
        let gradientLayer = CAGradientLayer()
        gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
        gradientLayer.endPoint = CGPoint(x: 0.5, y: -0.5)
        gradientLayer.type = .conic
        // Change the white value of the center color to adjust the center highlight brightness as desired
        gradientLayer.colors = [UIColor.black.cgColor, UIColor(white: 0.9, alpha: 1.0).cgColor, UIColor.black.cgColor]
        // Tweak the 1st and 3rd number as desired.
        // The closer to 0.5 the darker the cone edges will be.
        // The further from 0.5 the lighter the cone edges will be.
        gradientLayer.locations = [ 0.38, 0.5, 0.62 ]
        gradientLayer.frame = bounds
        gradientLayer.mask = shape
        layer.insertSublayer(gradientLayer, at: 0)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

// Set whatever size you want for the view
PlaygroundPage.current.liveView = ConeView(frame: CGRect(x: 0, y: 0, width: 200, height: 100))

Here's the result (the black border is not part of the view, just part of the screen capture):

enter image description here

Upvotes: 0

Related Questions