Multiline UIButton autosize per line

2.2k Views Asked by At

I want to use the standard UIButton but the text I want to put on the UIButton has...

  1. ... multiple lines (each line is different string) and ...
  2. ... each line should have different font and size that ...
  3. ... automatically resizes font to fit the button width (not height to make it a bit easier)

enter image description here

So, even tough I would like to set the (preferred) font size for each line, I want the font-size to automatically size down so that each separate line fits nicely in the UIButton (=same behaviour as the UILabel AutoShrink / Minimum Font Scale).

What I don't want:

I do not want to start adding UILabels to the UIButton (as subview for example) or using IB put UILabels on a scene and just draw an UIButton around it (why: I want the standard UIButton highlighting behaviour)

What I want:

A clean solution using an attributed string, that given the width scales down the font (updates the attributed string I guess), line-by-line if required.

My idea, implement a function like this:

    func addToAttributedString(attString : NSMutableAttributedString, plainString : String, font : UIFont, preferredSize : CGFloat, maxWidth : CGFloat)

And then I could make the attributed string by calling this with text 1,2,3... and insert a newline (\n) between them.

Any ideas?

3

There are 3 best solutions below

5
On

sizeToFit() will help you to adjust height as the text.

    var str : NSMutableAttributedString = NSMutableAttributedString(string: "Bla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla\nBla")
    str.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(20), range: NSRange(location: 13,length: 3))
    button.setAttributedTitle(str, forState: UIControlState.Normal)
    button.titleLabel!.lineBreakMode = .ByWordWrapping
    button.titleLabel?.textAlignment = .Center
    button.titleLabel?.adjustsFontSizeToFitWidth = true
    button.sizeToFit()
    button.layoutIfNeeded()

Working with your code too :

    var attrString = NSMutableAttributedString()
    addNewLineToAttributedString(attrString, plainString: "Big title", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    addNewLineToAttributedString(attrString, plainString: "Smaller text", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    addNewLineToAttributedString(attrString, plainString: "Smaller smaller text", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    addNewLineToAttributedString(attrString, plainString: "Smaller Smaller Smaller text", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    addNewLineToAttributedString(attrString, plainString: "Smaller Smaller Smaller text", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    addNewLineToAttributedString(attrString, plainString: "Big title", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    addNewLineToAttributedString(attrString, plainString: "Big title", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    addNewLineToAttributedString(attrString, plainString: "Smaller Smaller Smaller text", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    addNewLineToAttributedString(attrString, plainString: "Smaller Smaller Smaller text", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    addNewLineToAttributedString(attrString, plainString: "Smaller Smaller Smaller text", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    addNewLineToAttributedString(attrString, plainString: "Smaller Smaller Smaller text", font: UIFont.systemFontOfSize(10), preferredSize: 50, maxWidth: 100)
    button.setAttributedTitle(attrString, forState: UIControlState.Normal)
    button.titleLabel!.lineBreakMode = .ByWordWrapping
    button.titleLabel?.textAlignment = .Center
    button.titleLabel?.adjustsFontSizeToFitWidth = true
    button.sizeToFit()
    button.layoutIfNeeded()

enter image description here

1
On

You can achieve it using Storyboard it self. Just perform this steps

1: Change Button Type from System to Custome .

enter image description here

2: Change Button Title From plain to attributed.

3: Enter the text in textArea and press Alt + Enter when u need a newLine. see image it will break my text into 3 line .

enter image description here

4: Now set line break mode to Character Wrap.

enter image description here

NOTE: If you don't see the text in different line in storyboard change the text align from Align natural to Align left. set as select portion in image

enter image description here

5: Now select individual line and set font. U can also change the textColor.

enter image description here

6: Repeat Step5 for each line.

here is my simulator output:

enter image description here

0
On

Figured out a solution (have a look at my blog http://www.hixfield.net/blog/2015/06/multiline-uibutton-with-each-line-resized-to-fit-width/ for more info)

class ViewController: UIViewController {

    //the button that we are formatting
    @IBOutlet weak var btn: UIButton!

    //setup our button in the will appear function
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        setupBtn()
    }

    //function that does the magic
    func setupBtn() {
        btn.titleLabel?.lineBreakMode=NSLineBreakMode.ByWordWrapping
        var attrString = NSMutableAttributedString()
        var attr = [NSFontAttributeName : UIFont.systemFontOfSize(10)]
        attrString += (NSMutableAttributedString(string : "Big title", font: UIFont.systemFontOfSize(50), maxWidth: 100)! + "\n" )
        attrString += (NSMutableAttributedString(string : "Smaller text", font: UIFont.systemFontOfSize(50), maxWidth: 100)!  + "\n" )
        attrString += (NSMutableAttributedString(string : "Smaller smaller text", font: UIFont.systemFontOfSize(80), maxWidth: 100)! + "\n" )
        btn.setAttributedTitle(attrString, forState: UIControlState.Normal)
    }
}

//************************

extension NSMutableAttributedString {
    /**Makes an attributes string with the specified (plain) string and font but resizes the font smaller
    to fit the width if required. Can return nil, if there is no way to make it fit*/
    convenience init?(string : String, font : UIFont, maxWidth : CGFloat){
        self.init()
        for var size = font.pointSize ; size>1 ; size-- {
            let attrs = [NSFontAttributeName : font.fontWithSize(size)]
            let attrString = NSAttributedString(string: string, attributes: attrs)
            if attrString.size().width <= maxWidth {
                self.setAttributedString(attrString)
                return
            }
        }
        return nil
    }
}

//************************

public func += (inout left: NSMutableAttributedString, right: NSAttributedString) {
    left.appendAttributedString(right)
}

public func + (left: NSAttributedString, right: NSAttributedString) -> NSAttributedString {
    var result  = NSMutableAttributedString(attributedString: right)
    result.appendAttributedString(right)
    return result
}

public func + (left: NSAttributedString, right: String) -> NSAttributedString {
    var result  = NSMutableAttributedString(attributedString: left)
    result.appendAttributedString(NSAttributedString(string: right))
    return result
}

enter image description here