iOS: ridimensionamento del pulsante UIB in base alla lunghezza del testo


134

Nel builder di interfacce, tenendo premuto Command+ =si ridimensionerà un pulsante per adattarlo al testo. Mi chiedevo se fosse possibile farlo a livello di codice prima che il pulsante fosse aggiunto alla vista.

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];

1
Nota per questa domanda molto antica: oggi (2017), imposta il vincolo su "maggiore o uguale" e fa tutto. (Notate la risposta sotto di Tad.)
Fattie,

@Fattie Un vincolo " maggiore o uguale " non è sufficiente per la compatibilità con AutoLayout. Al giorno d'oggi (2017+) si dovrebbe o avvolgere il pulsante dentro qualcosa che ridimensiona correttamente (Bartłomiej Semańczyk risposta), oppure si dovrebbe creare una sottoclasse del UIButton, come in stackoverflow.com/a/50575588/1033581
Cœur

ciao @ Cœur - ci sono molte sottigliezze per il autolayout. È possibile che stai pensando a una situazione leggermente diversa? Tuttavia, per fortuna, ti sbagli di grosso, provalo !! Basta mettere un UIButton su un controller di visualizzazione. ovviamente imposta il centro orizzontale e verticale. Quindi aggiungi un vincolo> = 100 di larghezza. funziona perfettamente con l'ultimo iOS.
Fattie,

1
(Per chi non conosce il layout automatico googling qui ........... solo TBC non è necessario aggiungere nulla e UIButton (anche UILabel, ecc.) Ridimensionerà automaticamente la larghezza con modifiche al testo. vincola correttamente la posizione H / V.)
Fattie

Dimostrazione @Fattie . In particolare, in fase di esecuzione, si comporta in questo modo: github.com/Coeur/autolayoutButton/blob/master/buttonSelfSizing/…
Cœur

Risposte:


1

In Xcode 4.5 e versioni successive, questo può essere fatto ora usando ' Auto-layout / Vincoli '.

I principali vantaggi sono che:

  1. Non è necessario impostare programmaticamente frame!
  2. Se fatto bene, non è necessario preoccuparsi di ripristinare i frame per i cambiamenti di orientamento.
  3. Inoltre, le modifiche al dispositivo non devono disturbarti (leggi, non è necessario codificare separatamente per schermi di dimensioni diverse).

Alcuni svantaggi:

  1. Non compatibile con le versioni precedenti: funziona solo per iOS 6 e versioni successive.
  2. Devi familiarizzare (ma risparmierai tempo in seguito).

La cosa più bella è che ci concentriamo sulla dichiarazione di un intento come:

  • Voglio che questi due pulsanti abbiano la stessa larghezza; o
  • Ho bisogno che questa vista sia centrata verticalmente e si estenda fino a un massimo di 10 punti dal bordo della superview; o anche,
  • Voglio che questo pulsante / etichetta si ridimensioni in base all'etichetta che sta visualizzando!

Ecco un semplice tutorial per essere introdotti al layout automatico.

Per maggiori dettagli .

All'inizio ci vuole un po 'di tempo, ma sembra che ne varrà la pena.


3
Ho quasi perso questa risposta - ha davvero bisogno di molti più voti. Con questa soluzione è possibile evitare l'incorporamento di nomi e dimensioni dei caratteri o chiamate solo per iOS 7. Ho semplicemente impostato il vincolo del lato su cui volevo estendere il mio UIButton e il gioco è fatto.
Carl

Quasi una soluzione perfetta, purtroppo non funziona se usi titleEdgeInsets: /
Zoltán

130
Questa è una risposta scadente: descrive in dettaglio i vantaggi di AutoLayout stesso, piuttosto che come AutoLayout può essere applicato in questa situazione per risolvere il problema del richiedente.
mattsven,

4
Mi chiedevo se fosse possibile farlo programmaticamente, dice la risposta ...
Juan Boero,

I vincoli di layout automatico di @JuanPabloBoeroAlvarez possono essere specificati in Interface Builder e / o programmaticamente.
Slipp D. Thompson,

130

In UIKit, ci sono aggiunte alla classe NSString per ottenere da un determinato oggetto NSString la dimensione che occuperà quando viene eseguito il rendering in un determinato font.

Docs era qui . Ora è qui sotto obsoleto.

In breve, se vai:

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

... avrai impostato la cornice del pulsante all'altezza e alla larghezza della stringa che stai eseguendo il rendering.

Probabilmente vorrai sperimentare un po 'di spazio buffer attorno a quel CGSize, ma inizierai nel posto giusto.


4
Sembra che l'imbottitura predefinita di iOS sia 12px, 8px.
Ben Lachman,

19
sizeWithFont è obsoleto in iOS 7.0. Usa sizeWithAttributes: invece
carmen_munich,

6
Consiglio vivamente di non giocare più con i frame, ma SOLO con i vincoli.
Gil Sand,

@GilSand C'è un modo per usare l'interfaccia builder per questo?
Huggie,

Sì, una ricerca su Google dovrebbe darti molti risultati utili
Gil Sand,

101

Il modo per farlo nel codice è:

[button sizeToFit];

Se stai eseguendo la sottoclasse e desideri aggiungere ulteriori regole, puoi sostituire:

- (CGSize)sizeThatFits:(CGSize)size;

5
sizeToFit funziona perfettamente su controlli standard come UIButton o UILabel. Se lo stai facendo su un UIView personalizzato dovrai implementarlo -(CGSize)sizeThatFits:(CGSize)size.
Cameron Lowell Palmer,

4
Funziona solo se lo metti dopo aver impostato il testo, altrimenti non ci sono abbastanza informazioni su quanto dovrebbe essere grande: [button setTitle:@"Your text here" forState:UIControlStateNormal]; [button sizeToFit]; Inoltre, se aggiorni il testo in un secondo momento, non dimenticare di chiamarlo di nuovo.
Juan Boero,

Assicurati di impostare il testo solo in questo modo: [myButton setTitle: @ "my title" forState: UIControlStateNormal]; Per me non ha funzionato fino a quando non ho ripetuto questa affermazione.
Darius Miliauskas,

7
sizeToFit()non rispetta le inserzioni del bordo del titolo.
Kelin

32

Se il tuo pulsante è stato creato con Interface Builder e stai modificando il titolo nel codice, puoi farlo:

[self.button setTitle:@"Button Title" forState:UIControlStateNormal];
[self.button sizeToFit];

1
E puoi farlo anche se il tuo pulsante è stato creato con il codice.
Markus,

22

Swift:

La cosa importante qui è quando lo chiamerai .sizeToFit(), dovrebbe essere dopo aver impostato il testo da visualizzare in modo che possa leggere le dimensioni necessarie, se in seguito aggiorni il testo, quindi chiamalo di nuovo.

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

18

Per eseguire ciò utilizzando il layout automatico, prova a impostare un vincolo di larghezza variabile:

Vincolo di larghezza

Potrebbe anche essere necessario modificare il proprio Content Hugging Prioritye Content Compression Resistance Priorityper ottenere i risultati necessari.


UILabel si auto-dimensiona completamente automaticamente :

Questo UILabel è semplicemente impostato per essere centrato sullo schermo (solo due vincoli, orizzontale / verticale):

Cambia larghezze in modo totalmente automatico:

Non è necessario impostare alcuna larghezza o altezza: è totalmente automatico.

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Notare che i quadratini gialli sono semplicemente attaccati ("spaziatura" di zero). Si spostano automaticamente quando UILabel si ridimensiona.

L'aggiunta di un vincolo "> =" imposta una larghezza minima per UILabel:

inserisci qui la descrizione dell'immagine


16

Se vuoi ridimensionare il testo rispetto al pulsante, puoi usare ...

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;

3
* aggiustaFontSizeToFitWidth
Daniel Bang

8

sizeToFit non funziona correttamente. anziché:

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

puoi anche aggiungere contentInsetal pulsante:

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


Dai documenti Apple relativi a sizeToFit: // chiama sizeThatFits: con i limiti della vista corrente e modifica la dimensione dei limiti. Quindi funziona correttamente, devi solo impostarlo dopo aver impostato il testo.
Markus,

5

Semplicemente:

  1. Creare UIView come wrapper con layout automatico per visualizzare in giro.
  2. Metti UILabeldentro quell'involucro. Aggiungi vincoli che attaccheranno la tua etichetta ai bordi del wrapper.
  3. Inserisci UIButtonnel tuo wrapper, quindi aggiungi semplicemente gli stessi vincoli che hai fatto per UILabel.
  4. Goditi il ​​tuo pulsante di ridimensionamento automatico insieme al testo.

1
Funziona bene con l'accessibilità?
jasonszhao,

4

Rapido 4.2

Grazie a Dio, questo risolto. Dopo aver impostato un testo sul pulsante, è possibile recuperare intrinsicContentSize che è la dimensione naturale da una vista UIV (il documento ufficiale è qui ). Per UIButton, puoi usarlo come di seguito.

button.intrinsicContentSize.width

Per tua informazione, ho regolato la larghezza per farla apparire correttamente.

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

Simulatore

UIButtons con intrinsicContentSize

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


1
Potresti dare un po 'più contesto? In che modo la tua riga di codice si collega al codice nella domanda? Potresti anche riassumere cosa fa il codice che suggerisci? Fornire il link solo come una spiegazione non è il formato giusto per questo sito (vedi meta.stackexchange.com/a/8259/266197 per motivi), ma se aggiungessi qualche spiegazione e contesto, la tua risposta diventerebbe più preziosa!
NOh,

3

Ho alcune esigenze aggiuntive legate a questo post che sizeToFitnon risolvono. Avevo bisogno di mantenere il pulsante centrato nella posizione originale, indipendentemente dal testo. Inoltre, non voglio mai che il pulsante sia più grande della sua dimensione originale.

Quindi, layout il pulsante per la sua dimensione massima, salvo quella dimensione nel controller e utilizzo il seguente metodo per ridimensionare il pulsante quando il testo cambia:

+ (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];
 }

1

Questa classe di pulsanti con altezza viene ridimensionata automaticamente per il testo (per Xamarin ma può essere riscritta per un'altra lingua)

[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);
         }
     }
}

1

Per qualche motivo, func sizeToFit()non funziona per me. La mia configurazione è che sto usando un pulsante all'interno di a UITableViewCelle sto usando il layout automatico.

Ciò che ha funzionato per me è:

  1. ottenere il vincolo di larghezza
  2. ottenere la intrinsicContentSizelarghezza perché in base a questo documento il layout automatico non è a conoscenza di intrinsicContentSize. Impostare il vincolo di intrinsicContentSizelarghezza sulla larghezza

inserisci qui la descrizione dell'immagine

Ecco due titoli dal Debug View Hierachry inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine


0

Ad essere sincero, penso che sia davvero un peccato che non ci sia una semplice casella di controllo nello storyboard per dire che vuoi ridimensionare i pulsanti per adattarli al testo. Beh ... qualunque cosa.

Ecco la soluzione più semplice utilizzando lo storyboard.

  1. Posiziona UIView e metti vincoli per esso. Esempio: inserisci qui la descrizione dell'immagine
  2. Posiziona UILabel all'interno di UIView. Imposta i vincoli per collegarlo ai bordi di UIView.

  3. Posiziona il tuo UIButton all'interno di UIView. Imposta gli stessi vincoli per collegarlo ai bordi di UIView.

  4. Impostare 0 per il numero di righe di UILabel. inserisci qui la descrizione dell'immagine

  5. Impostare gli sbocchi.

    @IBOutlet var button: UIButton!
    @IBOutlet var textOnTheButton: UILabel!
  1. Ottieni un titolo lungo, lungo, lungo.

    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. On viewDidLoad imposta il titolo sia per UILabel che per UIButton.

    override func viewDidLoad() {
        super.viewDidLoad()
        textOnTheButton.text = someTitle
        button.setTitle(someTitle, for: .normal)
        button.titleLabel?.numberOfLines = 0
    }
  1. Eseguirlo per assicurarsi che il pulsante sia ridimensionato e possa essere premuto.

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.