iOS: UIButton-Größe entsprechend der Textlänge ändern

134

Wenn Sie im Interface Builder Command+ =gedrückt halten, wird die Größe einer Schaltfläche an den Text angepasst. Ich habe mich gefragt, ob dies programmgesteuert möglich ist, bevor die Schaltfläche zur Ansicht hinzugefügt wurde.

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button.titleLabel setFont:[UIFont fontWithName:@"Arial-BoldMT" size:12]];
[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
// I need to know the width needed to accomodate NSStringVariable
[button setTitle:NSStringVariable forState:UIControlStateNormal]; 
// So that I can set the width property of the button before addSubview
[button setFrame:CGRectMake(10, 0, width, fixedHeight)];
[subNavigation addSubview:button];
Oh Danny Boy
quelle
1
Hinweis für diese sehr alte Frage: Heutzutage (2017) setzen Sie die Einschränkung einfach auf "größer als oder gleich" und es macht alles. (Beachten Sie die Antwort unten von Tad.)
Fattie
@Fattie Eine Einschränkung " größer oder gleich " reicht für die Kompatibilität mit AutoLayout nicht aus . Heutzutage (2017+) sollte man entweder den Knopf in etwas einwickeln, dessen Größe richtig geändert wurde (Antwort von Bartłomiej Semańczyk), oder man sollte den UIButton unterklassifizieren, wie in stackoverflow.com/a/50575588/1033581
Cœur
hi @ Cœur - es gibt viele Feinheiten beim Autolayout. Ist es möglich, dass Sie an eine etwas andere Situation denken? Zum Glück liegen Sie jedoch völlig falsch - probieren Sie es einfach aus !! Legen Sie einfach einen UIButton auf einen View Controller. Stellen Sie offensichtlich die horizontale und vertikale Mitte ein. Fügen Sie dann eine Breitenbeschränkung> = 100 hinzu. es funktioniert perfekt in der neuesten iOS.
Fattie
1
(Für alle, die noch kein Autolayout-Googeln haben ........... nur TBC, Sie müssen überhaupt nichts hinzufügen, und UIButton (auch UILabel usw.) wird bei Textänderungen automatisch auf die Breite angepasst Einschränkung der H / V-Position.)
Fattie
@ Fattie Demonstration . Insbesondere zur Laufzeit verhält es sich so: github.com/Coeur/autolayoutButton/blob/master/buttonSelfSizing/…
Cœur

Antworten:

1

In Xcode 4.5 und höher kann dies jetzt mithilfe von ' Auto-Layouting / Constraints ' erfolgen.

Hauptvorteile sind:

  1. Sie müssen Frames überhaupt nicht programmgesteuert einstellen!
  2. Wenn Sie es richtig machen, müssen Sie sich nicht darum kümmern, Frames für Orientierungsänderungen zurückzusetzen.
  3. Außerdem müssen Sie Geräteänderungen nicht stören (lesen Sie, Sie müssen nicht separat für verschiedene Bildschirmgrößen codieren).

Einige Nachteile:

  1. Nicht abwärtskompatibel - funktioniert nur für iOS 6 und höher.
  2. Sie müssen sich vertraut machen (spart aber später Zeit).

Das Coolste ist, dass wir uns darauf konzentrieren können, eine Absicht zu erklären, wie zum Beispiel:

  • Ich möchte, dass diese beiden Tasten die gleiche Breite haben. oder
  • Ich brauche diese Ansicht, um vertikal zentriert zu sein und mich auf maximal 10 Punkte vom Rand der Übersicht zu erstrecken. oder auch,
  • Ich möchte, dass die Größe dieser Schaltfläche / Beschriftung entsprechend der angezeigten Beschriftung geändert wird!

Hier ist ein einfaches Tutorial, um sich mit dem automatischen Layout vertraut zu machen.

Für weitere Details .

Zuerst dauert es einige Zeit, aber es sieht so aus, als ob sich die Mühe lohnt.

Codebrand
quelle
3
Ich habe diese Antwort fast verpasst - es braucht wirklich viel mehr Stimmen. Mit dieser Lösung kann das Einbetten von Schriftfacennamen und -größen oder Nur-iOS 7-Aufrufen vermieden werden. Ich habe einfach die Einschränkung der Seite festgelegt, auf der mein UIButton erweitert werden soll, und das war's.
Carl
Fast eine perfekte Lösung, leider funktioniert es nicht, wenn Sie titleEdgeInsets verwenden: /
Zoltán
130
Dies ist eine schlechte Antwort - sie beschreibt die Vorzüge von AutoLayout selbst und nicht, wie AutoLayout in dieser Situation angewendet werden kann, um das Problem des Fragestellers zu lösen.
Mattsven
4
Ich habe mich gefragt, ob dies programmatisch möglich ist, sagt die Antwort ...
Juan Boero
@JuanPabloBoeroAlvarez Auto Layout-Einschränkungen können im Interface Builder und / oder programmgesteuert angegeben werden.
Slipp D. Thompson
130

In UIKit gibt es Ergänzungen zur NSString-Klasse, um von einem bestimmten NSString-Objekt die Größe zu erhalten, die es beim Rendern in einer bestimmten Schriftart benötigt.

Docs war hier . Jetzt ist es hier unter Veraltet.

Kurz gesagt, wenn Sie gehen:

CGSize stringsize = [myString sizeWithFont:[UIFont systemFontOfSize:14]]; 
//or whatever font you're using
[button setFrame:CGRectMake(10,0,stringsize.width, stringsize.height)];

... Sie haben den Rahmen der Schaltfläche auf die Höhe und Breite der Zeichenfolge eingestellt, die Sie rendern.

Sie werden wahrscheinlich mit etwas Pufferplatz um diese CGSize experimentieren wollen, aber Sie werden an der richtigen Stelle beginnen.

Dan Ray
quelle
4
Es sieht so aus, als ob die Standardauffüllung für iOS 12px, 8px ist.
Ben Lachman
19
sizeWithFont ist in iOS 7.0 veraltet. Verwenden Sie sizeWithAttributes: stattdessen
carmen_munich
6
Ich empfehle dringend, nicht mehr mit Frames zu spielen, sondern NUR mit Einschränkungen.
Gil Sand
@ GilSand Gibt es eine Möglichkeit, den Interface Builder dafür zu verwenden?
Huggie
Ja, eine Google-Suche sollte Ihnen viele nützliche Ergebnisse liefern
Gil Sand
101

Der Weg, dies im Code zu tun, ist:

[button sizeToFit];

Wenn Sie eine Unterklasse bilden und zusätzliche Regeln hinzufügen möchten, können Sie Folgendes überschreiben:

- (CGSize)sizeThatFits:(CGSize)size;
Daniel Wood
quelle
5
sizeToFit funktioniert perfekt mit Standardsteuerelementen wie UIButton oder UILabel. Wenn Sie dies in einem benutzerdefinierten UIView tun, müssen Sie es implementieren -(CGSize)sizeThatFits:(CGSize)size.
Cameron Lowell Palmer
4
Es funktioniert nur, wenn Sie es nach dem Festlegen des Textes einfügen. Andernfalls gibt es nicht genügend Informationen darüber, wie groß es sein sollte: [button setTitle:@"Your text here" forState:UIControlStateNormal]; [button sizeToFit]; Auch wenn Sie den Text später aktualisieren, vergessen Sie nicht, ihn erneut aufzurufen.
Juan Boero
Stellen Sie sicher, dass Sie den Text nur so festlegen: [myButton setTitle: @ "my title" forState: UIControlStateNormal]; Für mich hat es nicht funktioniert, bis ich diese Aussage wiederholt habe.
Darius Miliauskas
7
sizeToFit()Titelkanteneinfügungen werden nicht berücksichtigt.
Kelin
32

Wenn Ihre Schaltfläche mit Interface Builder erstellt wurde und Sie den Titel im Code ändern, können Sie Folgendes tun:

[self.button setTitle:@"Button Title" forState:UIControlStateNormal];
[self.button sizeToFit];
Guptron
quelle
1
Und das können Sie auch, wenn Ihre Schaltfläche mit Code erstellt wurde.
Markus
22

Schnell:

Das Wichtigste dabei ist, wann Sie das aufrufen .sizeToFit(). Dies sollte nach dem Einstellen des anzuzeigenden Texts geschehen, damit es die erforderliche Größe lesen kann. Wenn Sie den Text später aktualisieren, rufen Sie ihn erneut auf.

var button = UIButton()
button.setTitle("Length of this text determines size", forState: UIControlState.Normal)
button.sizeToFit()
self.view.addSubview(button)
Juan Boero
quelle
18

Um dies mithilfe des automatischen Layouts zu erreichen, legen Sie eine Einschränkung für die variable Breite fest:

Breitenbeschränkung

Sie können auch Ihre anpassen müssen Content Hugging Priorityund Content Compression Resistance Prioritydie Ergebnisse , die Sie brauchen.


UILabel passt sich automatisch an :

Dieses UILabel wird einfach so eingestellt, dass es auf dem Bildschirm zentriert ist (nur zwei Einschränkungen, horizontal / vertikal):

Die Breite ändert sich vollautomatisch:

Sie müssen nicht festlegen müssen jede Breite oder Höhe - es ist vollautomatisch.

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Beachten Sie, dass die kleinen gelben Quadrate einfach angebracht werden ("Abstand" von Null). Sie werden automatisch verschoben, wenn die Größe des UILabel geändert wird.

Durch Hinzufügen einer "> =" - Einschränkung wird eine Mindestbreite für das UILabel festgelegt:

Geben Sie hier die Bildbeschreibung ein

Bisschen
quelle
16

Wenn Sie die Größe des Textes im Gegensatz zur Schaltfläche ändern möchten, können Sie ...

button.titleLabel.adjustsFontSizeToFitWidth = YES;
button.titleLabel.minimumScaleFactor = .5;
// The .5 value means that I will let it go down to half the original font size 
// before the texts gets truncated

// note, if using anything pre ios6, you would use I think
button.titleLabel.minimumFontSize = 8;
Logan
quelle
3
* passtFontSizeToFitWidth an
Daniel Bang
8

sizeToFit funktioniert nicht richtig. stattdessen:

myButton.size = myButton.sizeThatFits(CGSize.zero)

Sie können contentInsetder Schaltfläche auch Folgendes hinzufügen :

myButton.contentEdgeInsets = UIEdgeInsetsMake(8, 8, 4, 8)

Hashem Aboonajmi
quelle
Aus Apple-Dokumenten zu sizeToFit: // ruft sizeThatFits: mit aktuellen Ansichtsgrenzen auf und ändert die Grenzgröße. Damit es richtig funktioniert, müssen Sie es erst einstellen, nachdem Sie den Text festgelegt haben.
Markus
5

Einfach:

  1. Erstellen Sie UIViewals Wrapper mit automatischem Layout für Ansichten in der Umgebung.
  2. Legen Sie UILabelinnerhalb dieser Wrapper. Fügen Sie Einschränkungen hinzu, mit denen Ihr Etikett an den Kanten des Deckblatts befestigt wird.
  3. Legen Sie es UIButtonin Ihren Wrapper und fügen Sie dann einfach die gleichen Einschränkungen hinzu, für die Sie es getan haben UILabel.
  4. Genießen Sie Ihre automatische Schaltfläche zusammen mit Text.
Bartłomiej Semańczyk
quelle
1
Spielt dies gut mit der Zugänglichkeit?
Jasonszhao
4

Swift 4.2

Gott sei Dank, das gelöst. Nachdem Sie einen Text für die Schaltfläche festgelegt haben, können Sie intrinsicContentSize, die natürliche Größe, aus einer UIView abrufen (das offizielle Dokument befindet sich hier ). Für UIButton können Sie es wie unten verwenden.

button.intrinsicContentSize.width

Zu Ihrer Information habe ich die Breite angepasst, damit sie richtig aussieht.

button.frame = CGRect(fx: xOffset, y: 0.0, width: button.intrinsicContentSize.width + 18, height: 40)

Simulator

UIButtons mit intrinsicContentSize

Quelle: https://riptutorial.com/ios/example/16418/get-uibutton-s-size-strictly-based-on-its-text-and-font

nator333
quelle
1
Könnten Sie etwas mehr Kontext geben? In welcher Beziehung steht Ihre Codezeile zum Code in der Frage? Können Sie auch zusammenfassen, was der von Ihnen vorgeschlagene Code bewirkt? Nur den Link als Erklärung anzugeben, ist nicht das richtige Format für diese Site (siehe meta.stackexchange.com/a/8259/266197 für Gründe), aber wenn Sie eine Erklärung und einen Kontext hinzufügen würden, würde Ihre Antwort wertvoller werden!
NOhs
3

Ich habe einige zusätzliche Bedürfnisse im Zusammenhang mit diesem Beitrag, sizeToFitdie nicht gelöst werden können. Ich musste die Schaltfläche unabhängig vom Text an ihrer ursprünglichen Position zentrieren. Ich möchte auch nie, dass der Knopf größer als seine ursprüngliche Größe ist.

Also gestalte ich die Schaltfläche auf ihre maximale Größe, speichere diese Größe im Controller und verwende die folgende Methode, um die Größe der Schaltfläche zu ändern, wenn sich der Text ändert:

+ (void) sizeButtonToText:(UIButton *)button availableSize:(CGSize)availableSize padding:(UIEdgeInsets)padding {    

     CGRect boundsForText = button.frame;

    // Measures string
    CGSize stringSize = [button.titleLabel.text sizeWithFont:button.titleLabel.font];
    stringSize.width = MIN(stringSize.width + padding.left + padding.right, availableSize.width);

    // Center's location of button
    boundsForText.origin.x += (boundsForText.size.width - stringSize.width) / 2;
    boundsForText.size.width = stringSize.width;
    [button setFrame:boundsForText];
 }
raider33
quelle
1

Diese Schaltflächenklasse mit automatischer Größenanpassung für Text (für Xamarin, kann jedoch für eine andere Sprache umgeschrieben werden)

[Register(nameof(ResizableButton))]
public class ResizableButton : UIButton
{
    NSLayoutConstraint _heightConstraint;
    public bool NeedsUpdateHeightConstraint { get; private set; } = true;

    public ResizableButton(){}

    public ResizableButton(UIButtonType type) : base(type){}

    public ResizableButton(NSCoder coder) : base(coder){}

    public ResizableButton(CGRect frame) : base(frame){}

    protected ResizableButton(NSObjectFlag t) : base(t){}

    protected internal ResizableButton(IntPtr handle) : base(handle){}

    public override void LayoutSubviews()
    {
        base.LayoutSubviews();
        UpdateHeightConstraint();
        InvalidateIntrinsicContentSize();
    }

    public override void SetTitle(string title, UIControlState forState)
    {
        NeedsUpdateHeightConstraint = true;
        base.SetTitle(title, forState);
    }

    private void UpdateHeightConstraint()
    {
        if (!NeedsUpdateHeightConstraint)
            return;
        NeedsUpdateHeightConstraint = false;
        var labelSize = TitleLabel.SizeThatFits(new CGSize(Frame.Width - TitleEdgeInsets.Left - TitleEdgeInsets.Right, float.MaxValue));

        var rect = new CGRect(Frame.X, Frame.Y, Frame.Width, labelSize.Height + TitleEdgeInsets.Top + TitleEdgeInsets.Bottom);

        if (_heightConstraint != null)
            RemoveConstraint(_heightConstraint);

        _heightConstraint = NSLayoutConstraint.Create(this, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1, rect.Height);
        AddConstraint(_heightConstraint);
    }

     public override CGSize IntrinsicContentSize
     {
         get
         {
             var labelSize = TitleLabel.SizeThatFits(new CGSize(Frame.Width - TitleEdgeInsets.Left - TitleEdgeInsets.Right, float.MaxValue));
             return new CGSize(Frame.Width, labelSize.Height + TitleEdgeInsets.Top + TitleEdgeInsets.Bottom);
         }
     }
}
elamaunt
quelle
1

Aus irgendeinem Grund func sizeToFit()funktioniert das bei mir nicht. Mein Setup ist, dass ich eine Schaltfläche in a verwende UITableViewCellund das automatische Layout verwende.

Was für mich funktioniert hat ist:

  1. Holen Sie sich die Breitenbeschränkung
  2. Holen Sie sich die intrinsicContentSizeBreite, weil laut diesem Dokument das automatische Layout nicht bekannt ist intrinsicContentSize. Stellen Sie die Breitenbeschränkung auf die intrinsicContentSizeBreite ein

Geben Sie hier die Bildbeschreibung ein

Hier sind zwei Titel aus dem Debug View Hierachry Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Loozie
quelle
0

Um ehrlich zu sein, finde ich es wirklich schade, dass es im Storyboard kein einfaches Kontrollkästchen gibt, das besagt, dass Sie die Größe der Schaltflächen ändern möchten, um den Text aufzunehmen. OK Was solls.

Hier ist die einfachste Lösung mit Storyboard.

  1. Platzieren Sie UIView und legen Sie Einschränkungen dafür fest. Beispiel: Geben Sie hier die Bildbeschreibung ein
  2. Platzieren Sie UILabel in UIView. Legen Sie Einschränkungen fest, um es an Kanten von UIView anzuhängen.

  3. Platzieren Sie Ihren UIButton in UIView. Legen Sie dieselben Einschränkungen fest, um es an die Ränder von UIView anzuhängen.

  4. Setzen Sie 0 für die Anzahl der Zeilen von UILabel. Geben Sie hier die Bildbeschreibung ein

  5. Richten Sie die Steckdosen ein.

    @IBOutlet var button: UIButton!
    @IBOutlet var textOnTheButton: UILabel!
  1. Holen Sie sich einen langen, langen, langen Titel.

    let someTitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  1. Legen Sie in viewDidLoad den Titel sowohl für UILabel als auch für UIButton fest.

    override func viewDidLoad() {
        super.viewDidLoad()
        textOnTheButton.text = someTitle
        button.setTitle(someTitle, for: .normal)
        button.titleLabel?.numberOfLines = 0
    }
  1. Führen Sie es aus, um sicherzustellen, dass die Größe der Schaltfläche geändert wird und gedrückt werden kann.

Geben Sie hier die Bildbeschreibung ein

Shalugin
quelle