Ben Souchet
Ben Souchet

Reputation: 1534

Freely positioning text inside a UILabel programmatically in Swift

I have created a subclass of UILabel to change the alignment/position of the text inside my UILabels but the text is always top aligned. I read lot of topics but I didn't find someone this the same problem has mine.

It looks like the y origin in drawText func of my subclass is ignored when I call super.drawText(in: r). I say that because origin.x, size.width and size.height of the rect I send to super.drawText() work as expected

Screenshot of the result I currently get with the code below

Screenshot of what I want

This is my UILabel subclass :

class Label : UILabel {

    enum VerticalAlignment {
        case top
        case middle
        case bottom
    }

    enum HorizontalAlignment {
        case left
        case center
        case right
    }

    var verticalAlignment: VerticalAlignment = .middle {
        didSet { setNeedsDisplay() }
    }

    var horizontalAlignment: HorizontalAlignment = .center {
        didSet { setNeedsDisplay() }
    }

    override public func textRect(forBounds bounds: CGRect, limitedToNumberOfLines: Int) -> CGRect {
        var rect: CGRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: limitedToNumberOfLines)

        rect = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: rect.size.width, height: rect.size.height)

        switch verticalAlignment {
            case .top:
                break
            case .middle:
                rect.origin.y += ((bounds.size.height - rect.size.height) / 2.0)
            case .bottom:
                rect.origin.y += (bounds.size.height - rect.size.height)
        }
        switch horizontalAlignment {
            case .left:
                break
            case .center:
                rect.origin.x += ((bounds.size.width - rect.size.width) / 2.0)
            case .right:
                rect.origin.x += (bounds.size.width - rect.size.width)
        }
        return rect
    }

    override func drawText(in rect: CGRect) {
        let r = self.textRect(forBounds: rect, limitedToNumberOfLines: self.numberOfLines)
        super.drawText(in: r)
    }

}

How I created my label :

func addtestLabel() {
    var label: Label = Label()

    label.text = "NICE LABEL!"
    label.textColor = hexToUIColor(hex: "#ffffff")
    label.frame = CGRect(x: 35, y: self.view.bounds.size.height - 120, width: self.view.bounds.size.width - 70, height: 75)
    label.backgroundColor = hexToUIColor(hex: "#000000")
    label.font = UIFont(name: "Savu-Condensed", size: label.frame.size.height / 2.0)
    label.lineBreakMode = .byWordWrapping
    label.numberOfLines = 1

    self.view.addSubview(label)
}

Upvotes: 0

Views: 925

Answers (1)

agibson007
agibson007

Reputation: 4373

Your code is fine except for a single line. When creating your label you cannot set

label.lineBreakMode = .byWordWrapping

and

label.numberOfLines = 1

I don't know why it breaks but it is not logical to wrap lines when there is only one so maybe something does not trigger.

You could either make the number of lines 0 or a number greater than 1. Another option is to delete the .byWordWrapping. Either of these things will give you the result you are expecting.

Upvotes: 1

Related Questions