How to Draw a Speech Bubble in Swift 2.0

Here’s a quick example of how to create a custom UIView to display a speech bubble in Swift 2.0 just like this:

swift uiview speech bubble

Step 1: Subclass UIView

Firstly, you need to subclass UIView as follows.

If you intended to use storyboard, ensure that you replace the fatalError line with super.init(coder: aDecoder)

class SpeechBubble: UIView {

    var color:UIColor = UIColor.grayColor()

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

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

Step 2: Override drawRect(rect: CGRect)

Do not call super.drawRect(rect) in this method!

  override func drawRect(rect: CGRect) {

    let rounding:CGFloat = rect.width * 0.02

    //Draw the main frame

    let bubbleFrame = CGRect(x: 0, y: 0, width: rect.width, height: rect.height * 2 / 3)
    let bubblePath = UIBezierPath(roundedRect: bubbleFrame, byRoundingCorners: UIRectCorner.AllCorners, cornerRadii: CGSize(width: rounding, height: rounding))

    //Color the bubbleFrame

    color.setStroke()
    color.setFill()
    bubblePath.stroke()
    bubblePath.fill()

    //Add the point

    let context = UIGraphicsGetCurrentContext()

    //Start the line

    CGContextBeginPath(context)
    CGContextMoveToPoint(context, CGRectGetMinX(bubbleFrame) + bubbleFrame.width * 1/3, CGRectGetMaxY(bubbleFrame))

    //Draw a rounded point

    CGContextAddArcToPoint(context, CGRectGetMaxX(rect) * 1/3, CGRectGetMaxY(rect), CGRectGetMaxX(bubbleFrame), CGRectGetMinY(bubbleFrame), rounding)

    //Close the line

    CGContextAddLineToPoint(context, CGRectGetMinX(bubbleFrame) + bubbleFrame.width * 2/3, CGRectGetMaxY(bubbleFrame))
    CGContextClosePath(context)

    //fill the color

    CGContextSetFillColorWithColor(context, color.CGColor)
    CGContextFillPath(context);

    }
 }

Step 3: Add a convenience initializer

Now we just need to add a convenience initializer so we can change the default color if required.

 required convenience init(withColor frame: CGRect, color:UIColor? = .None) {
    self.init(frame: frame)

    if let color = color {
        self.color = color
    }
 }

You can now instantiate a speech bubble with a color as follows:

 let speechBubble = SpeechBubble(withColor: CGRect(x: 0, y: 0, width: 200, height: 200), color: .redColor()) 

Of course, you can easily tweak this class to make it more flexible, such as customizing how rounded the edges are or adding a UILabel to display the number of comments etc.

Here’s the full class for reference:

class SpeechBubble: UIView {

    var color:UIColor = UIColor.grayColor()

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

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

    required convenience init(withColor frame: CGRect, color:UIColor? = .None) {
        self.init(frame: frame)

        if let color = color {
            self.color = color
        }
    }

    override func drawRect(rect: CGRect) {

        let rounding:CGFloat = rect.width * 0.02

        //Draw the main frame

        let bubbleFrame = CGRect(x: 0, y: 0, width: rect.width, height: rect.height * 2 / 3)
        let bubblePath = UIBezierPath(roundedRect: bubbleFrame, byRoundingCorners: UIRectCorner.AllCorners, cornerRadii: CGSize(width: rounding, height: rounding))

        //Color the bubbleFrame

        color.setStroke()
        color.setFill()
        bubblePath.stroke()
        bubblePath.fill()

        //Add the point

        let context = UIGraphicsGetCurrentContext()

        //Start the line

        CGContextBeginPath(context)
        CGContextMoveToPoint(context, CGRectGetMinX(bubbleFrame) + bubbleFrame.width * 1/3, CGRectGetMaxY(bubbleFrame))

        //Draw a rounded point

        CGContextAddArcToPoint(context, CGRectGetMaxX(rect) * 1/3, CGRectGetMaxY(rect), CGRectGetMaxX(bubbleFrame), CGRectGetMinY(bubbleFrame), rounding)

        //Close the line

        CGContextAddLineToPoint(context, CGRectGetMinX(bubbleFrame) + bubbleFrame.width * 2/3, CGRectGetMaxY(bubbleFrame))
        CGContextClosePath(context)

        //fill the color

        CGContextSetFillColorWithColor(context, color.CGColor)
        CGContextFillPath(context);
    }
}

Happy coding.