shoe
shoe

Reputation: 1080

How to create a triangle UIImage

How can I create a triangle UIImage? Here's how I'm doing it now, but it's not producing any image at all.

extension UIImage {

    static func triangle(side: CGFloat, color: UIColor)->UIImage {
        UIGraphicsBeginImageContextWithOptions(CGSize(width: side, height: side), false, 0)
        let ctx = UIGraphicsGetCurrentContext()!
        ctx.saveGState()

        ctx.beginPath()
        ctx.move(to: CGPoint(x: side / 2, y: 0))
        ctx.move(to: CGPoint(x: side, y: side))
        ctx.move(to: CGPoint(x: 0, y: side))
        ctx.move(to: CGPoint(x: side / 2, y: 0))
        ctx.closePath()

        ctx.setFillColor(color.cgColor)

        ctx.restoreGState()
        let img = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return img
    }
}

Upvotes: 3

Views: 839

Answers (3)

Rob
Rob

Reputation: 437917

You might just use UIBezierPath and not use CoreGraphics at all. Furthermore, nowadays we’d use UIGraphicsImageRenderer:

extension UIImage {
    static func triangle(side: CGFloat, color: UIColor) -> UIImage {
        return UIGraphicsImageRenderer(size: CGSize(width: side, height: side)).image { _ in
            let path = UIBezierPath()
            path.move(to: CGPoint(x: side / 2, y: 0))
            path.addLine(to: CGPoint(x: side, y: side))
            path.addLine(to: CGPoint(x: 0, y: side))
            path.close()

            color.setFill()
            path.fill()
        }
    }
}

For UIBezierPath version that still uses UIGraphicsBeginImageContextWithOptions, see previous revision of this answer.

Upvotes: 4

Duncan C
Duncan C

Reputation: 131471

OOPer told you about your lack of lines, and failure to actually draw the triangle.

In addition to that, I would suggest avoiding mucking around with graphics contexts. UIBezierPath makes it cleaner and easier to draw without having to worry about contexts. Also, you don't need to save and restore contexts since you are creating a temporary context and then immediately discarding it.

Here is a complete playground that draws your triangle (and fills and strokes the image rectangle so it's easier to see - you'd remove the stroke and probably fill the shape with UIColor.clear instead.)

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true


extension UIImage {

  static func triangle(side: CGFloat, color: UIColor)->UIImage {
    UIGraphicsBeginImageContextWithOptions(CGSize(width: side, height: side), false, 0)

    //First fill the bounds with white
    let rectPath = UIBezierPath(rect: CGRect(x:0, y: 0, width: side, height: side))
    UIColor.white.setFill() //Use clear if you want your image to only contain the triangle.
    rectPath.fill()

    //Now build the triangle path
    let path = UIBezierPath()
    path.move(to: CGPoint(x: side / 2, y: 0))
    path.addLine(to: CGPoint(x: side, y: side))
    path.addLine(to: CGPoint(x: 0, y: side))
    path.close() //Closing the path draws the final line
    color.setFill()
    path.fill()

    //Stroke the outer path in gray so you can see the bounds - remove if desired
    UIColor.gray.setStroke()
    rectPath.stroke()

    let img = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return img
  }
}

let imageView = UIImageView(frame: CGRect(x:0, y: 0, width: 200, height: 200))


PlaygroundPage.current.liveView = imageView
imageView.image = UIImage.triangle(side: 200.0, color: UIColor.yellow)

Upvotes: 0

OOPer
OOPer

Reputation: 47896

Your path does not contain any lines, so there's no region to fill. In addition you are not drawing the path.

Try something like this:

static func triangle(side: CGFloat, color: UIColor)->UIImage {
    UIGraphicsBeginImageContextWithOptions(CGSize(width: side, height: side), false, 0)
    let ctx = UIGraphicsGetCurrentContext()!
    ctx.saveGState()

    ctx.beginPath()
    ctx.move(to: CGPoint(x: side / 2, y: 0))
    //### Add lines
    ctx.addLine(to: CGPoint(x: side, y: side))
    ctx.addLine(to: CGPoint(x: 0, y: side))
    //ctx.addLine(to: CGPoint(x: side / 2, y: 0)) //### path is automatically closed
    ctx.closePath()

    ctx.setFillColor(color.cgColor)

    ctx.drawPath(using: .fill) //### draw the path

    ctx.restoreGState()
    let img = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return img
}

Upvotes: 5

Related Questions