Joel Rudsberg
Joel Rudsberg

Reputation: 85

Changing UILabel title in custom UIButton without positional change

I have a UIButton with the label on the left side and UIImageView on the right side. The button is used to open a UIPicker. When a value is picked in the picker the same value is shown in button title. When the title changes (or, more accurately, when to uilabel has a width that screws the UI up) the title and icon is moved and the UI does not look good.

When a title with too long text is used the word is clipped and when it's too short the alignment is messed up.

I've tried changing the label frame so it can be constant, whatever the text, and left aligning the text so the jumping stops. I added adjustsFontSizeToFitWidth = true which kind of works, but with a longer title the text will get too small. I've also tried recreating/rerendering the button when the title changes but all attempts fail.

lazy var sortButton = { () -> UIButton in
        let btn = UIButton()
        btn.addTarget(self, action: #selector(sortButtonPressed), for: .touchUpInside)
        btn.setTitle(NSLocalizedString("Sortera", comment: ""), for: .normal)
        btn.titleLabel?.text = btn.titleLabel?.text?.uppercased()
        btn.setImage(UIImage(named: "ios-down"), for: .normal)

        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.setTitleColor(Colors.FILTER_BUTTON_TEXT_COLOR, for: .normal)
        btn.titleLabel?.adjustsFontSizeToFitWidth = true
        btn.titleLabel?.font = UIFont(name: Fonts.AkzidenzGroteskProMd, size: 16)
        btn.backgroundColor = Colors.BUTTON_BACKGROUND_GRAY
        btn.imageView?.contentMode = .scaleAspectFit
        btn.imageEdgeInsets = UIEdgeInsets(top: 16, left: (btn.titleLabel?.frame.size.width)! - buttonInsideOffset/2, bottom: 16, right: -(btn.titleLabel?.frame.size.width)! + buttonInsideOffset/2)
        btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(btn.titleLabel?.frame.size.width)! + buttonInsideOffset, bottom: 0, right: (btn.titleLabel?.frame.size.width)! - buttonInsideOffset)
        return btn
    }()

I want all button to like like this, whatever the title text: enter image description here

However, when the text is too small it looks like this: enter image description here

or when it's too long:

enter image description here

Upvotes: 1

Views: 348

Answers (2)

Mussa Charles
Mussa Charles

Reputation: 4412

If you dont want button image to be shifted to the right or left then you have to constraint independent of the button's title label

     btn.imageView?.frame = CGRect(x: 0, y: 0, width: 20, height: 20) // Or any size you want

    // NB: I ommited left insets intentionally
    btn.imageEdgeInsets.top = 16
    btn.imageEdgeInsets.bottom = 16
    btn.imageEdgeInsets.right = 16

Then constraint your label dependent to the imageView position, that way only label size will change without affecting the position of image.

btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(btn.titleLabel?.frame.size.width)! + buttonInsideOffset, bottom: 0, right:   btn.imageView?.frame.width + 10)

Lastly, Since the frame is fixed size, I think you need to limit the font scaling factor to the min size you want and truncate the tail when that size is reached. If you don't want to truncate tail then you have to enable multiline titleLabel (Which I think you don't want this).

    btn.titleLabel?.minimumScaleFactor = 0.5 // Or whatever minimum scale you wish
    btn.titleLabel?.lineBreakMode = NSLineBreakMode.byTruncatingTail // Since button size is fixed and you want to limit font size then the best option is to truncate tail

Upvotes: 0

Ekramul Hoque
Ekramul Hoque

Reputation: 692

You can do it in many way but the simplest way :

Take a UIView and then others two-element (a label a imageView) set in this View and make it look like button then set constraint as you want. Then addTarget to label and do all functionality to that that target selector method.

Upvotes: 2

Related Questions