web-dev-qa-db-fra.com

iOS NSAttributedString sur UIButton

J'utilise iOS 6, les chaînes ainsi attribuées devraient être faciles à utiliser, non? Eh bien ... pas tellement.

Ce que je veux faire:

En utilisant une sous-classe personnalisée de UIButton (il ne fait rien de personnalisé pour titleLabel), je voudrais avoir un titre attribué à plusieurs lignes, à savoir:

  1. Toutes les majuscules (je réalise que cela ne fait pas partie des attributs) sur la première ligne
  2. Gras sur la première ligne
  3. souligné sur la première ligne
  4. Poids "normal" sur la deuxième ligne
  5. Non souligné sur la deuxième ligne
  6. Centré sur les deux lignes

J'ai réussi à obtenir les # 1 à 5 jusqu'à présent (du moins, je le pensais, mais les tests actuels génèrent des erreurs avec du texte multiligne), mais lorsque j'ai essayé de faire quelque chose (n'importe quoi!) Pour obtenir le texte à centrer, mon application ne cesse de planter. Lorsque j'essaie de faire fonctionner les 6 éléments (à l'aide de différentes méthodes), le message d'erreur suivant apparaît:

Terminating app due to uncaught exception 
'NSInternalInconsistencyException', reason: 
'NSAttributedString invalid for autoresizing, 
it must have a single spanning paragraph style
(or none) with a non-wrapping lineBreakMode.'

D'après ce que j'ai essayé, il semble que je puisse avoir l'une des options suivantes, mais pas les deux:

  1. Une étiquette multiligne centrée
  2. Une étiquette attribuée

Je peux vivre avec l'un ou l'autre si je doit, mais je ne peux pas croire que je ne peux pas avoir ce qui semble être un concept assez simple.

Quelqu'un peut-il s'il vous plaît me dire ce que je me suis trompé?

Voici la dernière itération du code que j'essaie:

NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setAlignment:NSTextAlignmentCenter];
[style setLineBreakMode:NSLineBreakByWordWrapping];

UIFont *font1 = [UIFont fontWithName:@"HelveticaNeue-Medium" size:20.0f];
UIFont *font2 = [UIFont fontWithName:@"HelveticaNeue-Light"  size:20.0f];
NSDictionary *dict1 = @{NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle),  
                        NSFontAttributeName:font1};
NSDictionary *dict2 = @{NSUnderlineStyleAttributeName:@(NSUnderlineStyleNone),    
                        NSFontAttributeName:font2};

NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] init];
[attString appendAttributedString:[[NSAttributedString alloc] initWithString:@"LINE 1\n"    attributes:dict1]];
[attString appendAttributedString:[[NSAttributedString alloc] initWithString:@"line 2"      attributes:dict2]];
[[self buttonToStyle] setAttributedTitle:attString forState:UIControlStateNormal];
[[[self buttonToStyle] titleLabel] setNumberOfLines:0];
[[[self buttonToStyle] titleLabel] setLineBreakMode:NSLineBreakByWordWrapping];
55
mbm29414

Il me semble que vous avez oublié dans votre code d'utiliser l'objet "style" que vous avez configuré. Vous l'avez simplement instancié. Vous devriez modifier votre code pour ressembler à ceci:

NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setAlignment:NSTextAlignmentCenter];
[style setLineBreakMode:NSLineBreakByWordWrapping];

UIFont *font1 = [UIFont fontWithName:@"HelveticaNeue-Medium" size:20.0f];
UIFont *font2 = [UIFont fontWithName:@"HelveticaNeue-Light"  size:20.0f];
NSDictionary *dict1 = @{NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle),
                        NSFontAttributeName:font1,
                        NSParagraphStyleAttributeName:style}; // Added line
NSDictionary *dict2 = @{NSUnderlineStyleAttributeName:@(NSUnderlineStyleNone),
                        NSFontAttributeName:font2,
                        NSParagraphStyleAttributeName:style}; // Added line

NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] init];
[attString appendAttributedString:[[NSAttributedString alloc] initWithString:@"LINE 1\n"    attributes:dict1]];
[attString appendAttributedString:[[NSAttributedString alloc] initWithString:@"line 2"      attributes:dict2]];
[self.resolveButton setAttributedTitle:attString forState:UIControlStateNormal];
[[self.resolveButton titleLabel] setNumberOfLines:0];
[[self.resolveButton titleLabel] setLineBreakMode:NSLineBreakByWordWrapping];

Notez que j'ai seulement ajouté les lignes qui définissent le NSParagraphStyleAttributeName .. tout le reste est identique .. et voici ce que j'ai pour le bouton:

enter image description here

Et le voici dans Swift 3.0

let style = NSMutableParagraphStyle()
style.alignment = .center
style.lineBreakMode = .byWordWrapping

guard
    let font1 = UIFont(name: "HelveticaNeue-Medium", size: 20),
    let font2 = UIFont(name: "HelveticaNeue-Light", size: 20)  else { return }

let dict1:[String:Any] = [
    NSUnderlineStyleAttributeName:NSUnderlineStyle.styleSingle.rawValue,
    NSFontAttributeName:font1,
    NSParagraphStyleAttributeName:style
]

let dict2:[String:Any] = [
    NSUnderlineStyleAttributeName:NSUnderlineStyle.styleNone.rawValue,
    NSFontAttributeName:font2,
    NSParagraphStyleAttributeName:style
]

let attString = NSMutableAttributedString()
attString.append(NSAttributedString(string: "LINE 1", attributes: dict1))
attString.append(NSAttributedString(string: "line 2", attributes: dict2))

button.setAttributedTitle(attString, for: .normal)
button.titleLabel?.numberOfLines = 0
button.titleLabel?.lineBreakMode = .byWordWrapping
93
ccwasden

Avec Swift 4, vous pouvez utiliser l'implémentation de la sous-classe UIButton ci-dessous pour résoudre votre problème:

import UIKit

class CustomButton: UIButton {

    required init(title: String, subtitle: String) {
        super.init(frame: CGRect.zero)

        let style = NSMutableParagraphStyle()
        style.alignment = NSTextAlignment.center
        style.lineBreakMode = NSLineBreakMode.byWordWrapping

        let titleAttributes: [NSAttributedStringKey : Any] = [
            NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue,
            NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: UIFontTextStyle.largeTitle),
            NSAttributedStringKey.paragraphStyle : style
        ]
        let subtitleAttributes = [
            NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: UIFontTextStyle.body),
            NSAttributedStringKey.paragraphStyle : style
        ]

        let attributedString = NSMutableAttributedString(string: title, attributes: titleAttributes)
        attributedString.append(NSAttributedString(string: "\n"))
        attributedString.append(NSAttributedString(string: subtitle, attributes: subtitleAttributes))

        setAttributedTitle(attributedString, for: UIControlState.normal)
        titleLabel?.numberOfLines = 0
        titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
    }

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

}

Utilisation:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = CustomButton(title: "Title", subtitle: "Subtitle")
        button.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(button)

        let horizontalConstraint = button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        let verticalConstraint = button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])
    }

}

Si vous avez réellement besoin d'un bouton de type system, vous pouvez utiliser le code suivant:

import UIKit

extension UIButton {

    static func customSystemButton(title: String, subtitle: String) -> UIButton {            
        let style = NSMutableParagraphStyle()
        style.alignment = NSTextAlignment.center
        style.lineBreakMode = NSLineBreakMode.byWordWrapping

        let titleAttributes: [NSAttributedStringKey : Any] = [
            NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue,
            NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: UIFontTextStyle.largeTitle),
            NSAttributedStringKey.paragraphStyle : style
        ]
        let subtitleAttributes = [
            NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: UIFontTextStyle.body),
            NSAttributedStringKey.paragraphStyle : style
        ]

        let attributedString = NSMutableAttributedString(string: title, attributes: titleAttributes)
        attributedString.append(NSAttributedString(string: "\n"))
        attributedString.append(NSAttributedString(string: subtitle, attributes: subtitleAttributes))

        let button = UIButton(type: UIButtonType.system)
        button.setAttributedTitle(attributedString, for: UIControlState.normal)
        button.titleLabel?.numberOfLines = 0
        button.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping

        return button
    }

}

Utilisation:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = UIButton.customSystemButton(title: "Title", subtitle: "Subtitle")
        button.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(button)

        let horizontalConstraint = button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        let verticalConstraint = button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])
    }

}

Les deux captures d'écran ci-dessous montrent l'affichage des résultats pour la sous-classe UIButton (à gauche) et pour le bouton de type system (à droite):

enter image description here

5
Imanou Petit