Search code examples
autolayoutuilabeltooltipdrawrectdrawtextinrect

UILabel Subclass - Custom 'drawRect' method causes text to cut off / not show


I am creating a simple 'tooltip' subclass that is a rounded rectangle and a small triangle that will be 'anchored' to another view.

I created a UILabel subclass and am overriding 'drawRect' to shrink the main label area and draw a triangle.

The 'roundRect' represents the rounded rectangle portion that should contain the full text.

This all works great, except that I am having trouble getting the full text to be shown within the 'roundRect'. It appears that the text just doesn't show up for the last line (i.e. where the triangle now occupies).

One other thing to note, I am using auto layout and have setup the constraints for the 'tooltipLabel' to fill the screen as needed. This works as expected, as reducing/adding more text shows the appropriate sized tooltip. But it seems like I need to somehow inform the 'tooltip' or 'auto layout' that the text should be fitted into the 'roundRect' portion of the 'tooltipLabel'.

Screenshot #1 uses this 'drawTextInRect' method and you can see that the full text is being shown, but overlaps into the 'triangle' area (plus it has no insets, which is not the desired look):

    override public func drawTextInRect(rect: CGRect) {
        super.drawTextInRect(rect)
//        super.drawTextInRect(UIEdgeInsetsInsetRect(rect, UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)))
    }

Screenshot #2 uses this 'drawTextInRect' method with the desired insets (also adds the triangleHeight so that space is not written into with text) and you can see that it cuts off the 2nd and 3rd lines of text, as they don't 'fit' within the 'roundRect' bounds:

    override public func drawTextInRect(rect: CGRect) {
//        super.drawTextInRect(rect)
        super.drawTextInRect(UIEdgeInsetsInsetRect(self.roundRect, UIEdgeInsets(top: 10, left: 10, bottom: 10+self.triangleHeight, right: 10)))
    }

Here is the 'drawRect' override:

override public func drawRect(rect: CGRect) {
    self.roundRect = CGRect(x: rect.minX, y: rect.minY, width: rect.width, height: rect.height-self.triangleHeight)
    self.triangleBezier.moveToPoint(CGPoint(x: self.roundRect.midX-self.triangleWidth/2, y: self.roundRect.maxY))
    self.triangleBezier.addLineToPoint(CGPoint(x: rect.midX, y: rect.maxY))
    self.triangleBezier.addLineToPoint(CGPoint(x: self.roundRect.midX+self.triangleWidth/2, y: self.roundRect.maxY))      

    self.triangleBezier.closePath()
    self.roundRectBezier = UIBezierPath(roundedRect: self.roundRect, cornerRadius: 5.0)
    self.roundRectBezier.appendPath(self.triangleBezier)
    self.tooltipColor.setFill()
    self.roundRectBezier.fill()

    super.drawRect(rect)
}

Screenshot #1 Screenshot #2


Solution

  • I would actually not be subclassing UILabel for this, and make your own tooltip class composed of the outer view and the internal label with auto layout constraints. The internal label determines the whole height of the view.

    Something like this with appropriately rounded off corners/triangle: enter image description here

    Alternatively, use UITextView instead if you want to assign padding: Adding space/padding to a UILabel