Swift - UIButton avec deux lignes de texte

93

Je me demandais s'il était possible de créer un UIButton avec deux lignes de texte. J'ai besoin que chaque ligne ait une taille de police différente. La première ligne sera de 17 points et la seconde de 11 points. J'ai essayé de jouer avec deux étiquettes à l'intérieur d'un UIButton, mais je ne peux pas les faire rester dans les limites du bouton.

J'essaie de faire tout cela dans le générateur d'interface utilisateur, et non par programme.

Merci

Scott
la source

Réponses:

249

Il y a deux questions.

Je me demandais s'il est possible de créer un UIButton avec deux lignes de texte

Cela est possible en utilisant le storyboard ou par programme.

Storyboard:

Changez le «Mode saut de ligne» en Retour à la ligne de caractère ou Retour à la ligne et utilisez la touche Alt / Option + Entrée pour entrer une nouvelle ligne dans le champ Titre de l'UIButton.

entrez la description de l'image ici

Par programme:

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

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

J'ai besoin que chaque ligne ait une taille de police différente 1

Le pire des cas est que vous pouvez utiliser une UIButtonclasse personnalisée et y ajouter deux étiquettes.

La meilleure façon est d'utiliser NSMutableAttributedString. Notez que cela ne peut être réalisé que par programme.

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 plus âgé

@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)

    }

Production

entrez la description de l'image ici

Shamsudheen TK
la source
2
Fonctionne très bien. Je me demande maintenant s'il existe un moyen de centrer le texte sur chaque ligne et s'il existe un moyen d'insérer plus d'espace entre les deux lignes.
Scott
3
vous pouvez aligner le texte des deux lignes au centre. écrivez le code suivant btnTwoLine? .titleLabel? .textAlignment = NSTextAlignment.Center ou faites-le en utilisant le fichier de storyboard (section de contrôle-> Alignement)
Shamsudheen TK
puis-je savoir quel est le but de mettre plus de lignes entre les deux?
Shamsudheen TK
Cela dépend de la taille du bouton. Si le bouton est gros, alors les deux lignes de texte seront en plein milieu, avec beaucoup d'espace en haut et en bas. Ce n'est pas le look que je recherchais.
Scott
vous devez appliquer quelques astuces ici :) vous pouvez mettre plus de lignes entre les deux en utilisant plusieurs \ n. Je veux dire, "bonjour \ n \ n \ nthere" vous donnera trois espaces. cependant, n'oubliez pas de modifier votre code var newlineRange: NSRange = buttonText.rangeOfString ("\ n \ n \ n")
Shamsudheen TK
22

Je cherchais à peu près le même sujet, sauf que je n'ai pas besoin de deux tailles de police différentes. Dans le cas où quelqu'un recherche une solution simple:

    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.
la source
12

J'ai remarqué un problème dans la plupart des solutions qui consiste à faire du mode saut de ligne sur "Character Wrap", la deuxième ligne sera alignée sur la première ligne

Pour centrer toutes les lignes. changez simplement le titre De Plain à Attribué et vous pouvez ensuite centrer chaque ligne

titre centré attribué

Musa almatri
la source
6

changez le saut de ligne en retour à la ligne, sélectionnez votre bouton et dans l'inspecteur d'attributs, passez au saut de ligne et changez-le en retour à la ligne

entrez la description de l'image ici

Sabhay Sardana
la source
6

Syntaxe 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
la source
2
Je ne sais pas pourquoi, mais j'ai dû ajouter button.titleLabel? .numberOfLines = 0
budidino
Cela n'a pas fonctionné dans Swift 4 en premier. Besoin de définir "saut de ligne" sur "retour à la ligne". Thanks man :)
Karan Alangat
La réponse précédente originale est ci-dessous: stackoverflow.com/a/30679547/5318223
Kiril S.
5

J'ai corrigé cela et ma solution était uniquement dans le Storyboard.

Changements:

Il a ajouté dans Identity Inspector -> Attributs d'exécution définis par l'utilisateur (ces KeyPaths):

  • numberOfLines = 2
  • titleLabel.textAlignment = 1

Attributs d'exécution définis par l'utilisateur

J'ai ajouté ceci dans l'inspecteur d'attributs:

  • saut de ligne = retour à la ligne

Word wrap

A. Trejo
la source
2

Vous devez faire une partie de cela dans le code. vous ne pouvez pas définir 2 polices différentes dans IB. En plus de changer le mode de saut de ligne en retour à la ligne de caractère, vous avez besoin de quelque chose comme ceci pour définir le titre,

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
la source
1

Une façon de le faire est d'utiliser des étiquettes, je suppose. Je l'ai fait, et cela semble fonctionner correctement. Je pourrais créer ceci comme UIButton et ensuite exposer les étiquettes, je suppose. Je ne sais pas si cela a du sens.

    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
la source
0

mon chemin:

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
la source
0

Les solutions suggérées n'ont malheureusement pas fonctionné pour moi lorsque je voulais avoir un bouton mutliline dans un CollectionView. Puis un collègue m'a montré une solution de contournement que je voulais partager au cas où quelqu'un aurait le même problème - j'espère que cela aide! Créez une classe qui hérite de UIControl et étendez-la avec une étiquette, qui se comportera alors comme un bouton.

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
la source