Swift - UIButton com duas linhas de texto

93

Eu queria saber se é possível criar um UIButton com duas linhas de texto. Preciso que cada linha tenha um tamanho de fonte diferente. A primeira linha terá 17 pontos e a segunda terá 11 pontos. Tentei colocar dois rótulos dentro de um UIButton, mas não consigo fazer com que fiquem dentro dos limites do botão.

Estou tentando fazer tudo isso no construtor de interface do usuário, e não programaticamente.

obrigado

Scott
fonte

Respostas:

248

Existem duas questões.

Eu queria saber se é possível criar um UIButton com duas linhas de texto

Isso é possível usando o storyboard ou programaticamente.

Storyboard:

Mude o 'Line Break Mode' para Character Wrap ou Word Wrap e use Alt / Option + Enter para inserir uma nova linha no campo UIButton's Title.

insira a descrição da imagem aqui

Programaticamente:

override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        btnTwoLine?.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping;
}

Preciso que cada linha tenha um tamanho de fonte diferente 1

O pior caso é que você pode usar uma UIButtonclasse personalizada e adicionar dois rótulos a ela.

A melhor maneira é usar NSMutableAttributedString. Observe que isso pode ser feito apenas por meio de programação.

Swift 5:

@IBOutlet weak var btnTwoLine: UIButton?

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    //applying the line break mode
    textResponseButton?.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping;
    let buttonText: NSString = "hello\nthere"

    //getting the range to separate the button title strings
    let newlineRange: NSRange = buttonText.range(of: "\n")

    //getting both substrings
    var substring1 = ""
    var substring2 = ""

    if(newlineRange.location != NSNotFound) {
        substring1 = buttonText.substring(to: newlineRange.location)
        substring2 = buttonText.substring(from: newlineRange.location)
    }

    //assigning diffrent fonts to both substrings
    let font1: UIFont = UIFont(name: "Arial", size: 17.0)!
    let attributes1 = [NSMutableAttributedString.Key.font: font1]
    let attrString1 = NSMutableAttributedString(string: substring1, attributes: attributes1)

    let font2: UIFont = UIFont(name: "Arial", size: 11.0)!
    let attributes2 = [NSMutableAttributedString.Key.font: font2]
    let attrString2 = NSMutableAttributedString(string: substring2, attributes: attributes2)

    //appending both attributed strings
    attrString1.append(attrString2)

    //assigning the resultant attributed strings to the button
    textResponseButton?.setAttributedTitle(attrString1, for: [])
}

Swift mais velho

@IBOutlet weak var btnTwoLine: UIButton?

override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        //applying the line break mode
        btnTwoLine?.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping;

        var buttonText: NSString = "hello\nthere"

        //getting the range to separate the button title strings
        var newlineRange: NSRange = buttonText.rangeOfString("\n")

        //getting both substrings
        var substring1: NSString = ""
        var substring2: NSString = ""

        if(newlineRange.location != NSNotFound) {
            substring1 = buttonText.substringToIndex(newlineRange.location)
            substring2 = buttonText.substringFromIndex(newlineRange.location)
        }

        //assigning diffrent fonts to both substrings
        let font:UIFont? = UIFont(name: "Arial", size: 17.0)
        let attrString = NSMutableAttributedString(
            string: substring1 as String,
            attributes: NSDictionary(
                object: font!,
                forKey: NSFontAttributeName) as [NSObject : AnyObject])

        let font1:UIFont? = UIFont(name: "Arial", size: 11.0)
        let attrString1 = NSMutableAttributedString(
            string: substring2 as String,
            attributes: NSDictionary(
                object: font1!,
                forKey: NSFontAttributeName) as [NSObject : AnyObject])

        //appending both attributed strings
        attrString.appendAttributedString(attrString1)

        //assigning the resultant attributed strings to the button
        btnTwoLine?.setAttributedTitle(attrString, forState: UIControlState.Normal)

    }

Resultado

insira a descrição da imagem aqui

Shamsudheen TK
fonte
2
Funciona bem. Agora estou me perguntando se há alguma maneira de centralizar o texto em cada linha e se há alguma maneira de inserir mais espaço entre as duas linhas.
Scott
3
você pode alinhar as duas linhas de texto ao centro. escreva o seguinte código btnTwoLine? .titleLabel? .textAlignment = NSTextAlignment.Center ou faça-o usando o arquivo de storyboard (seção de controle-> Alinhamento)
Shamsudheen TK
posso saber qual é o propósito de colocar mais linhas no meio?
Shamsudheen TK
Depende do tamanho do botão. Se o botão for grande, as duas linhas de texto ficarão bem no meio, com muito espaço na parte superior e inferior. Esse não é o visual que eu estava procurando.
Scott
você tem que aplicar alguns truques aqui :) você pode colocar mais linhas no meio usando vários \ n. Quer dizer, "olá \ n \ n \ nthere" dará a você três espaços. no entanto, não se esqueça de modificar seu código var newlineRange: NSRange = buttonText.rangeOfString ("\ n \ n \ n")
Shamsudheen TK
22

Eu estava procurando quase o mesmo tópico, exceto que não preciso de dois tamanhos de fonte diferentes. Caso alguém esteja procurando uma solução simples:

    let button = UIButton()
    button.titleLabel?.numberOfLines = 0
    button.titleLabel?.lineBreakMode = .byWordWrapping
    button.setTitle("Foo\nBar", for: .normal)
    button.titleLabel?.textAlignment = .center
    button.sizeToFit()
    button.addTarget(self, action: #selector(rightBarButtonTapped), for: .allEvents)
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)
Nico S.
fonte
12

Notei um problema na maioria das soluções que é ao fazer o modo de quebra de linha para "Quebra de caracteres" a segunda linha será alinhada à esquerda com a primeira linha

Para fazer todas as linhas centralizadas. apenas altere o título De Simples para Atribuído e, em seguida, você pode tornar cada linha centralizada

título centralizado atribuído

Musa almatri
fonte
6

mude a quebra de linha para quebra de linha, selecione seu botão e no inspetor de atributos vá para quebra de linha e mude para quebra de linha

insira a descrição da imagem aqui

Sabhay Sardana
fonte
6

Sintaxe SWIFT 3

let str = NSMutableAttributedString(string: "First line\nSecond Line")
str.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 17), range: NSMakeRange(0, 10))
str.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 12), range: NSMakeRange(11, 11))
button.setAttributedTitle(str, for: .normal)
Maksim Kniazev
fonte
2
não sei por que, mas tive que adicionar button.titleLabel? .numberOfLines = 0
budidino
Não funcionou no Swift 4 primeiro. É necessário definir "quebra de linha" para "quebra de linha". Obrigado cara :)
Karan Alangat
a resposta anterior original está abaixo: stackoverflow.com/a/30679547/5318223
Kiril S.
5

Eu consertei isso e minha solução era apenas no Storyboard.

Alterar:

Ele adicionou no Identity Inspector -> Atributos de tempo de execução definidos pelo usuário (estes KeyPaths):

  • numberOfLines = 2
  • titleLabel.textAlignment = 1

Atributos de tempo de execução definidos pelo usuário

Eu adicionei isso no inspetor de atributos:

  • quebra de linha = quebra de linha

Quebra de linha

A. Trejo
fonte
2

Você precisa fazer isso no código. você não pode definir 2 fontes diferentes em IB. Além de alterar o modo de quebra de linha para quebra de caracteres, você precisa de algo assim para definir o título,

override func viewDidLoad() {
        super.viewDidLoad()
        var str = NSMutableAttributedString(string: "First line\nSecond Line")
        str.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(17), range: NSMakeRange(0, 10))
        str.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(12), range: NSMakeRange(11, 11))
        button.setAttributedTitle(str, forState: .Normal)

    }
rdelmar
fonte
1

Uma maneira de fazer isso é com rótulos, eu acho. Eu fiz isso e parece funcionar bem. Eu poderia criar isso como um UIButton e então expor os rótulos, eu acho. Não sei se isso faz sentido.

    let firstLabel = UILabel()

    firstLabel.backgroundColor = UIColor.lightGrayColor()
    firstLabel.text = "Hi"
    firstLabel.textColor = UIColor.blueColor()
    firstLabel.textAlignment = NSTextAlignment.Center
    firstLabel.frame = CGRectMake(0, testButton.frame.height * 0.25, testButton.frame.width, testButton.frame.height * 0.2)
    testButton.addSubview(firstLabel)

    let secondLabel = UILabel()

    secondLabel.backgroundColor = UIColor.lightGrayColor()
    secondLabel.textColor = UIColor.blueColor()
    secondLabel.font = UIFont(name: "Arial", size: 12)
    secondLabel.text = "There"
    secondLabel.textAlignment = NSTextAlignment.Center
    secondLabel.frame = CGRectMake(0, testButton.frame.height * 0.5, testButton.frame.width, testButton.frame.height * 0.2)
    testButton.addSubview(secondLabel)
Scott
fonte
0

o meu caminho:

func setButtonTitle(title: String, subtitle: String, button: UIButton){
        //applying the line break mode
        button.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping;
        let title = NSMutableAttributedString(string: title, attributes: Attributes.biggestLabel)
        let subtitle = NSMutableAttributedString(string: subtitle, attributes: Attributes.label)
        let char = NSMutableAttributedString(string: "\n", attributes: Attributes.biggestLabel)
        title.append(char)
        title.append(subtitle)
        button.setAttributedTitle(title, for: .normal)
    }
Nastassia
fonte
0

As soluções sugeridas infelizmente não funcionaram para mim quando eu queria ter um botão mutliline dentro de um CollectionView. Então, um colega me mostrou uma solução alternativa que eu gostaria de compartilhar caso alguém tenha o mesmo problema - espero que isso ajude! Crie uma classe que herda de UIControl e a estenda com um rótulo, que então se comportará de forma semelhante a um botão.

class MultilineButton: UIControl {

    let label: UILabel = {
        $0.translatesAutoresizingMaskIntoConstraints = false
        $0.numberOfLines = 0
        $0.textAlignment = .center
        return $0
    }(UILabel())

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

        addSubview(label)

        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
            label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
            label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
            label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
        ])
    }

    override var isHighlighted: Bool {
        didSet {
            backgroundColor = backgroundColor?.withAlphaComponent(isHighlighted ? 0.7 : 1.0)
            label.textColor = label.textColor.withAlphaComponent(isHighlighted ? 0.7 : 1.0)
        }
    }

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