web-dev-qa-db-fra.com

UIButton qui redimensionne pour correspondre à son titreLabel

J'ai une UIButton que j'ajoute à la vue de mon contrôleur de vue dans un storyboard. J'ajoute des contraintes de centrage pour le positionner et des contraintes d'espace pour limiter sa largeur. Dans le code j'ajoute:

self.button.titleLabel.numberOfLines = 0;
self.button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[self.button setTitle:@"A real real real real real real real real long long name." forState:UIControlStateNormal];
self.button.backgroundColor = [UIColor redColor];
self.button.titleLabel.backgroundColor = [UIColor blueColor];

Le résultat est présenté ci-dessous:

enter image description here

Je veux que le bouton s'adapte à son contenu. Comment puis-je faire ceci?

J'ai essayé 

[self.button sizeToFit];

et j'ai essayé de définir les priorités de contrainte de blocage automatique du contenu et de résistance à la compression.

J'ai également essayé de définir explicitement les variables contentEdgeInsets et titleEdgeInsets sur UIEdgeInsetsZero et d'appeler invalidateIntrinsicContentSize

J'ai également remarqué que si je place des caractères de nouvelle ligne dans la chaîne de titre, le bouton semble redimensionner pour s'adapter à son contenu.

J'utilise Xcode 6 et iOS 8 sur le simulateur iPhone 6.

26
Darren

Cela fonctionne, mais vous devez utiliser un bouton personnalisé, pas un type de système. Attribuez au bouton des contraintes de largeur et de hauteur et créez un objet IBOutlet pour la contrainte de hauteur (heightCon dans mon code) afin de pouvoir l'ajuster dans le code.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.button.titleLabel.numberOfLines = 0;
    self.button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
    [self.button setTitle:@"A real real real real real real real real long long name." forState:UIControlStateNormal];
    [self.button addTarget:self action:@selector(doStuff:) forControlEvents:UIControlEventTouchUpInside];
    self.button.backgroundColor = [UIColor redColor];
    self.button.titleLabel.backgroundColor = [UIColor blueColor];
    [self.button layoutIfNeeded]; // need this to update the button's titleLabel's size
    self.heightCon.constant = self.button.titleLabel.frame.size.height;
}

Après édition:

J'ai trouvé que vous pouvez aussi le faire plus simplement et avec un bouton système si vous créez une sous-classe et utilisez ce code,

@implementation RDButton

-(CGSize)intrinsicContentSize {
    return CGSizeMake(self.frame.size.width, self.titleLabel.frame.size.height);
}

La méthode intrinsicContentSize substituée est appelée lorsque vous définissez le titre. Vous ne devez pas définir de contrainte de hauteur dans ce cas.

18
rdelmar

Swift 4.x version de Kubba's answer:

Besoin de mettre à jour Line Break sous la forme Clip/WordWrap/ dans le générateur Interface avec les boutons correspondants.

class ResizableButton: UIButton {
    override var intrinsicContentSize: CGSize {
       let labelSize = titleLabel?.sizeThatFits(CGSize(width: frame.width, height: .greatestFiniteMagnitude)) ?? .zero
       let desiredButtonSize = CGSize(width: labelSize.width + titleEdgeInsets.left + titleEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom)

       return desiredButtonSize
    }
}
20
A. Buksha

J'ai le même problème. Cela se produit uniquement si UIButton's titleLabel a plusieurs lignes. Est-ce un bug dans UIKit?

Ma solution Swift:

class ResizableButton: UIButton {    
    override var intrinsicContentSize: CGSize {
        let labelSize = titleLabel?.sizeThatFits(CGSize(width: frame.size.width, height: CGFloat.greatestFiniteMagnitude)) ?? .zero
        let desiredButtonSize = CGSize(width: labelSize.width + titleEdgeInsets.left + titleEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom)

        return desiredButtonSize
    }
}
11
Kubba

J'ai eu du mal avec cela pendant un moment et j'ai fini par le faire fonctionner en sous-classant UIButton et en ajoutant ces deux fonctions

class GoalsButton: UIButton {

    override var intrinsicContentSize: CGSize {
        return self.titleLabel!.intrinsicContentSize
    }

    // Whever the button is changed or needs to layout subviews,
    override func layoutSubviews() {
        super.layoutSubviews()
        titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width
    }
}
3
Lord Fresh

Je suis loin de mon ordinateur et je ne peux donc pas ajouter le code pour l'instant, mais j'ai déjà trouvé une solution de contournement. 

Ce que vous pouvez faire est de créer une UILabel et d’ajouter une UITapGestureRecognizer à l’étiquette. Faites ce que vous voulez pour l'action du bouton en gérant l'événement tap. Et assurez-vous également que vous activez les interactions utilisateur sur la UILabel.

Cette étiquette se comportera désormais comme un bouton de redimensionnement automatique.

2
Vinay Jain

J'ai eu le même problème avec UIButton avec un texte multiligne, et il avait aussi une image. J'ai utilisé sizeThatFits: pour calculer la taille mais la mauvaise hauteur a été calculée.

Je ne l'ai pas fait UIButtonTypeCustom, au lieu de cela j'ai appelé sizeThatFits: sur le bouton titleLabel avec une taille avec une largeur plus petite (à cause de l'image dans le bouton):

CGSize buttonSize = [button sizeThatFits:CGSizeMake(maxWidth, maxHeight)];
CGSize labelSize = [button.titleLabel sizeThatFits:CGSizeMake(maxWidth - offset, maxHeight)]; // offset for image
buttonSize.height = labelSize.height;
buttonFrame.size = buttonSize;

Et puis j'ai utilisé la hauteur à partir de cette taille pour définir correctement le cadre du bouton, et cela a FONCTIONNÉ :)

Ils ont peut-être un problème avec le dimensionnement interne de UIButton.

1
BSevo

[self.button sizeToFit] devrait fonctionner si votre autolayout est désactivé. Si vous devez utiliser l'autolayout, d'autres suggestions (calcul de la largeur de ligne), etc.

1
Aamir

Ce que je suggère est de calculer la largeur du texte et de calculer le cadre par vous-même. Ce n'est pas compliqué quand même, commencez par la largeur du texte:

[NSString sizeWithFont:font];

Effectuez une opération de modification et vous découvrirez facilement le nombre de lignes du texte.

Notez que cette méthode concerne les versions antérieures à iOS7, iOS 7 et après que vous souhaitiez peut-être essayer.

[NSString sizeWithAttributes:aDictionary];
1
Xiangdong

Je devais invalider la taille du contenu intrinsèque lorsque les vues étaient affichées, puis calculer la hauteur du bouton pour la propriété intrinsicContentSize. 

Voici le code dans Swift3/Xcode 9

override func layoutSubviews() {
    self.invalidateIntrinsicContentSize()
    super.layoutSubviews()
}


override var intrinsicContentSize: CGSize {
    return CGSize(width: self.frame.size.width, height: titleLabel!.frame.size.height + contentEdgeInsets.top + contentEdgeInsets.bottom)
}
0
Maria

Remplacez le - (CGSize) intrinsicContentSize dans UIButton personnalisé comme indiqué ci-dessous.

Objectif c:

-(CGSize)intrinsicContentSize {
    CGSize titleLabelIntrinsicSize = [self.titleLabel intrinsicContentSize];
    return CGSizeMake(titleLabelIntrinsicSize.width + self.contentEdgeInsets.left + self.contentEdgeInsets.right, titleLabelIntrinsicSize.height + self.contentEdgeInsets.top + self.contentEdgeInsets.bottom);
}

Swfit:

override var intrinsicContentSize: CGSize {
        get {
            if let thisSize = self.titleLabel?.intrinsicContentSize {
                return CGSize(width: thisSize.width + self.contentEdgeInsets.left + self.contentEdgeInsets.right, height: thisSize.height + self.contentEdgeInsets.top + self.contentEdgeInsets.bottom)
            }
            return super.intrinsicContentSize
        }
    }
0
Bhavesh Patel