UIButton qui redimensionne pour s'adapter à son titleLabel
J'ai un 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 indiqué ci-dessous:
Je veux que le bouton soit à la taille de son contenu. Comment puis-je faire cela?
J'ai essayé
[self.button sizeToFit];
Et j'ai essayé de définir les priorités des contraintes de serrage de contenu et de résistance à la compression sur requis.
J'ai j'ai également essayé de définir explicitement 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 se redimensionner pour s'adapter à son contenu.
Je cours sur Xcode 6 et iOS 8 sur le simulateur iPhone 6.
11 réponses
J'ai eu à travailler, mais vous devez utiliser un bouton personnalisé, pas un type de système. Donnez au Bouton les contraintes de largeur et de hauteur, et faites un IBOutlet à la contrainte de hauteur (heightCon dans mon code) afin que vous puissiez 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 L'Édition:
J'ai trouvé que vous pouvez aussi le faire plus simplement, et avec un bouton système Si vous faites 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 remplacée est appelée lorsque vous définissez intitulé. Vous ne devez pas définir de contrainte de hauteur dans ce cas.
Swift 4.x version de Réponse de Kubba {[5]:
Besoin de mettre à jour saut de ligne comme Clip/WordWrap/ dans Interface builder aux 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
}
}
J'ai le même problème. Cela n'arrive que si UIButton
titleLabel
a plus d'une ligne. Est - ce un bug dans UIKit?
Ma solution rapide:
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
}
}
J'ai lutté 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
}
}
Je suis loin de mon ordinateur, donc je ne peux pas ajouter le code pour le moment, mais j'ai déjà trouvé une solution de contournement.
Ce que vous pouvez faire est de créer un UILabel
et ajouter un UITapGestureRecognizer
à l'étiquette. Faites ce que vous voulez pour l'action du bouton en manipulant l'événement tap. Et assurez-vous également d'activer les interactions utilisateur sur le UILabel
.
Cette étiquette se comporte désormais comme un bouton de redimensionnement automatique.
Ce que je suggère est de calculer la largeur du texte, et calculer le cadre par vous-même. Ce n'est pas compliqué de toute façon, obtenez d'abord la largeur du texte:
[NSString sizeWithFont:font];
Faites une opération de mod et vous trouverez facilement le nombre de lignes pour le texte.
Note Cette méthode est pour pré iOS7, pour iOS 7 et après vous voudrez peut-être essayer
[NSString sizeWithAttributes:aDictionary];
[auto.bouton sizeToFit] devrait fonctionner si votre Autolayout est désactivé. Si vous devez utiliser autolayout, d'autres suggestions (calcul de la largeur de la ligne), etc., sont plus appropriées.
J'ai eu le même problème avec UIButton
avec du texte multiliné, et il y avait aussi une image. J'ai utilisé sizeThatFits:
pour calculer la taille mais il a calculé une mauvaise hauteur.
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 (en raison 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 de cette taille pour définir correctement le cadre du bouton, et cela a fonctionné:)
Peut-être qu'ils ont un bug dans le dimensionnement interne de UIButton
.
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
}
}
J'ai dû invalider la taille du contenu intrinsèque lorsque les vues ont été disposé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)
}
class SFResizableButton: UIButton {
override var intrinsicContentSize: CGSize {
get {
var labelSize = CGSize.zero
if let text = titleLabel?.text, let font = titleLabel?.font {
labelSize.width = text.width(constrained: .greatestFiniteMagnitude, font: font)
} else if let att = titleLabel?.attributedText {
labelSize.width = att.width(constrained: .greatestFiniteMagnitude)
}
if let imageView = imageView {
labelSize.width = labelSize.width + imageView.frame.width
}
let desiredButtonSize = CGSize(width: ceil(labelSize.width) + titleEdgeInsets.left + titleEdgeInsets.right + imageEdgeInsets.left + imageEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom + imageEdgeInsets.top + imageEdgeInsets.bottom)
return desiredButtonSize
}
}
}
extesion String {
func width(constrained height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = (self as NSString).boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return boundingBox.width
}
}
extension NSAttributedString {
func width(constrained height: CGFloat) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)
return boundingBox.width
}
}