iOS: UIButton zmienia rozmiar w zależności od długości tekstu

134

W kreatorze interfejsów przytrzymanie Command+ =zmieni rozmiar przycisku, aby dopasować go do tekstu. Zastanawiałem się, czy można to zrobić programowo, zanim przycisk został dodany do widoku.

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];
Och, Danny Boy
źródło
1
Uwaga na to bardzo stare pytanie: obecnie (2017) wystarczy ustawić ograniczenie jako „większe lub równe” i robi to wszystko. (Zwróć uwagę na odpowiedź poniżej Tad.)
Fattie
@Fattie Ograniczenie „ większe lub równenie wystarcza do zapewnienia zgodności z układem AutoLayout. W dzisiejszych czasach (2017+) należy albo zawinąć przycisk wewnątrz czegoś, co poprawnie zmienia rozmiar (odpowiedź Bartłomiej Semańczyk), albo podklasować UIButton, jak w stackoverflow.com/a/50575588/1033581
Cœur
cześć @ Cœur - autoukładanie ma wiele subtelności. Możliwe, że myślisz o nieco innej sytuacji? Jednak na szczęście całkowicie się mylisz - po prostu spróbuj !! Po prostu umieść przycisk UIButton na kontrolerze widoku. oczywiście ustaw środek w poziomie i pionie. Następnie dodaj ograniczenie szerokości> = 100. działa idealnie w najnowszym iOS.
Fattie
1
(Dla każdego, kto nie ma doświadczenia z autoukładaniem google tutaj ........... tylko TBC, nie musisz w ogóle niczego dodawać, a UIButton (również UILabel itp.) Automatycznie zmieni rozmiar na szerokość wraz ze zmianami tekstu. Po prostu ograniczają odpowiednio pozycję H / V.)
Fattie
Demonstracja @Fattie . W szczególności zachowuje się w czasie wykonywania: github.com/Coeur/autolayoutButton/blob/master/buttonSelfSizing/ ...
Cœur

Odpowiedzi:

1

W Xcode 4.5 i nowszych można to teraz zrobić za pomocą opcji „ Automatyczne układanie / ograniczenia ”.

Główne zalety to:

  1. Nie musisz w ogóle programowo ustawiać ramek!
  2. Jeśli zrobisz to dobrze, nie musisz przejmować się resetowaniem ramek w celu zmiany orientacji.
  3. Ponadto zmiany urządzenia nie muszą Ci przeszkadzać (przeczytaj, nie musisz kodować osobno dla różnych rozmiarów ekranu).

Kilka wad:

  1. Brak kompatybilności wstecznej - działa tylko na iOS 6 i nowszych.
  2. Musisz się zapoznać (ale pozwoli to zaoszczędzić czas później).

Najfajniejsze jest to, że możemy skupić się na zadeklarowaniu zamiaru, takiego jak:

  • Chcę, aby te dwa przyciski miały tę samą szerokość; lub
  • Potrzebuję, aby ten widok był wyśrodkowany w pionie i rozciągał się maksymalnie do 10 punktów od krawędzi superwiewu; lub nawet,
  • Chcę, aby ten przycisk / etykieta zmienił rozmiar zgodnie z wyświetlaną etykietą!

Oto prosty samouczek wprowadzający do automatycznego układania.

Po więcej szczegółów .

Na początku zajmuje to trochę czasu, ale z pewnością wygląda na to, że będzie to warte wysiłku.

codeburn
źródło
3
Prawie przegapiłem tę odpowiedź - naprawdę potrzebuje dużo więcej pozytywnych głosów. Dzięki temu rozwiązaniu można uniknąć osadzania nazw i rozmiarów czcionek lub wywołań tylko dla iOS 7. Po prostu ustawiłem ograniczenie strony, po której chciałem, aby mój UIButton rozciągnął się i to wszystko.
Carl
Prawie idealne rozwiązanie, niestety nie działa, jeśli używasz titleEdgeInsets: /
Zoltán
130
To zła odpowiedź - szczegółowo opisuje zalety samego układu AutoLayout, a nie tego, w jaki sposób można go zastosować w tej sytuacji do rozwiązania problemu osoby pytającej.
mattsven
4
Zastanawiałem się, czy można to zrobić programowo, mówi odpowiedź ...
Juan Boero
Ograniczenia @JuanPabloBoeroAlvarez Auto Layout można określić w Interface Builder i / lub programowo.
Slipp D. Thompson
130

W UIKit istnieją dodatki do klasy NSString, aby uzyskać z danego obiektu NSString rozmiar, jaki zajmie przy renderowaniu w określonej czcionce.

Docs tutaj . Teraz jest tutaj obszarze Wycofane.

Krótko mówiąc, jeśli pójdziesz:

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

... ustawisz ramkę przycisku na wysokość i szerokość renderowanego ciągu.

Prawdopodobnie będziesz chciał poeksperymentować z przestrzenią buforową wokół tego CGSize, ale zaczniesz we właściwym miejscu.

Dan Ray
źródło
4
Wygląda na to, że domyślne wypełnienie w iOS to 12 pikseli, 8 pikseli.
Ben Lachman
19
sizeWithFont jest przestarzałe w systemie iOS 7.0. Zamiast tego użyj sizeWithAttributes:
carmen_munich
6
Zdecydowanie sugeruję, aby nie bawić się już ramkami, a TYLKO z ograniczeniami.
Gil Sand
@GilSand Czy istnieje sposób na użycie do tego narzędzia do tworzenia interfejsu?
przytulanie
Tak, wyszukiwanie w Google powinno dać wiele przydatnych wyników
Gil Sand,
101

Sposób na to w kodzie to:

[button sizeToFit];

Jeśli tworzysz podklasy i chcesz dodać dodatkowe reguły, możesz zmienić:

- (CGSize)sizeThatFits:(CGSize)size;
Daniel Wood
źródło
5
sizeToFit działa doskonale na standardowych kontrolkach, takich jak UIButton lub UILabel. Jeśli robisz to na niestandardowym UIView, musisz zaimplementować -(CGSize)sizeThatFits:(CGSize)size.
Cameron Lowell Palmer,
4
Działa tylko wtedy, gdy umieścisz go po ustawieniu tekstu, w przeciwnym razie nie ma wystarczających informacji o tym, jak duży powinien być: [button setTitle:@"Your text here" forState:UIControlStateNormal]; [button sizeToFit]; Również jeśli później zaktualizujesz tekst, nie zapomnij wywołać tego ponownie.
Juan Boero
Upewnij się, że ustawiłeś tekst tylko w ten sposób: [myButton setTitle: @ "my title" forState: UIControlStateNormal]; Dla mnie to nie zadziałało, dopóki nie powtórzyłem tego stwierdzenia.
Darius Miliauskas
7
sizeToFit()nie przestrzega wstawek krawędzi tytułów.
kelin
32

Jeśli twój przycisk został utworzony za pomocą Interface Builder i zmieniasz tytuł w kodzie, możesz to zrobić:

[self.button setTitle:@"Button Title" forState:UIControlStateNormal];
[self.button sizeToFit];
guptron
źródło
1
I możesz to zrobić, nawet jeśli twój przycisk został wykonany z kodem.
Markus
22

Szybki:

Ważną rzeczą jest to, kiedy zadzwonisz do programu .sizeToFit(), powinno to nastąpić po ustawieniu wyświetlania tekstu, aby mógł odczytać wymagany rozmiar, jeśli później zaktualizujesz tekst, wywołaj go ponownie.

var button = UIButton()
button.setTitle("Length of this text determines size", forState: UIControlState.Normal)
button.sizeToFit()
self.view.addSubview(button)
Juan Boero
źródło
18

Aby to osiągnąć za pomocą automatycznego układu, spróbuj ustawić zmienne ograniczenie szerokości:

Wiązanie szerokości

Konieczne może być również dostosowanie Content Hugging Priorityi Content Compression Resistance Priorityuzyskanie potrzebnych wyników.


UILabel zmienia rozmiar automatycznie :

Ten UILabel jest po prostu ustawiony na wyśrodkowanie ekranu (tylko dwa ograniczenia, poziome / pionowe):

Zmienia szerokości całkowicie automatycznie:

Nie musisz ustawiać żadnej szerokości ani wysokości - jest to całkowicie automatyczne.

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

Zwróć uwagę, że małe żółte kwadraty są po prostu dołączone („odstępy” równe zero). Przenoszą się automatycznie wraz ze zmianą rozmiaru UILabel.

Dodanie ograniczenia "> =" ustawia minimalną szerokość dla UILabel:

wprowadź opis obrazu tutaj

Berbeć
źródło
16

Jeśli chcesz zmienić rozmiar tekstu, a nie przycisku, możesz użyć ...

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
źródło
3
* adjustsFontSizeToFitWidth
Daniel Bang,
8

sizeToFit nie działa poprawnie. zamiast:

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

możesz również dodać contentInsetdo przycisku:

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

Hashem Aboonajmi
źródło
Z dokumentów Apple dotyczących sizeToFit: // wywołuje sizeThatFits: z bieżącymi granicami widoku i zmienia rozmiar granic. Więc działa poprawnie, po prostu musisz to ustawić po ustawieniu tekstu.
Markus
5

Po prostu:

  1. Stwórz UIView jako opakowanie z automatycznym układem widoków dookoła.
  2. Włóż UILabeldo tego opakowania. Dodaj wiązania, które przykleją etykietę do krawędzi opakowania.
  3. Umieść UIButtonw swoim opakowaniu, a następnie po prostu dodaj te same ograniczenia, co w przypadku UILabel.
  4. Ciesz się automatycznie dopasowanym przyciskiem wraz z tekstem.
Bartłomiej Semańczyk
źródło
1
Czy to współgra z dostępnością?
jasonszhao
4

Swift 4.2

Dzięki Bogu, to rozwiązane. Po ustawieniu tekstu na przycisku możesz pobrać intrinsicContentSize, który jest naturalnym rozmiarem z UIView (oficjalny dokument jest tutaj ). W przypadku UIButton możesz go używać jak poniżej.

button.intrinsicContentSize.width

Dla Twojej informacji dostosowałem szerokość, aby wyglądała prawidłowo.

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

Symulator

UIButtons z intrinsicContentSize

Źródło: https://riptutorial.com/ios/example/16418/get-uibutton-s-size-strictly-based-on-its-text-and-font

nator333
źródło
1
Czy mógłbyś podać trochę więcej kontekstu? Jaki jest związek twojego wiersza kodu z kodem w pytaniu? Czy mógłbyś również podsumować, co robi sugerowany przez Ciebie kod? Samo podanie linku jako wyjaśnienia nie jest właściwym formatem dla tej strony (patrz meta.stackexchange.com/a/8259/266197 z powodów), ale gdybyś dodał wyjaśnienie i kontekst, twoja odpowiedź stałaby się bardziej wartościowa!
NO,
3

Mam dodatkowe potrzeby związane z tym postem, które sizeToFitnie rozwiązują. Musiałem utrzymać przycisk wyśrodkowany w jego pierwotnej lokalizacji, niezależnie od tekstu. Nie chcę też, aby przycisk był większy niż jego oryginalny rozmiar.

Tak więc układam przycisk pod kątem jego maksymalnego rozmiaru, zapisuję ten rozmiar w kontrolerze i używam następującej metody, aby zmienić rozmiar przycisku po zmianie tekstu:

+ (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
źródło
1

Ta klasa przycisku z automatycznym dostosowywaniem wysokości dla tekstu (dla platformy Xamarin, ale można ją przepisać dla innego języka)

[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
źródło
1

Z jakiegoś powodu func sizeToFit()u mnie nie działa. Moja konfiguracja polega na używaniu przycisku wewnątrz a UITableViewCelli używam układu automatycznego.

Dla mnie zadziałało:

  1. pobierz ograniczenie szerokości
  2. uzyskać intrinsicContentSizeszerokość, ponieważ zgodnie z tym automatycznym układem dokumentu nie ma informacji o intrinsicContentSize. Ustaw ograniczenie intrinsicContentSizeszerokości na szerokość

wprowadź opis obrazu tutaj

Oto dwa tytuły z Debug View Hierachry wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

Loozie
źródło
0

Szczerze mówiąc, myślę, że to naprawdę wstyd, że w scenorysie nie ma prostego pola wyboru, aby powiedzieć, że chcesz zmienić rozmiar przycisków, aby pomieścić tekst. Cóż ... nieważne.

Oto najprostsze rozwiązanie wykorzystujące scenorys.

  1. Umieść UIView i umieść dla niego ograniczenia. Przykład: wprowadź opis obrazu tutaj
  2. Umieść UILabel w UIView. Ustaw wiązania, aby dołączyć go do krawędzi UIView.

  3. Umieść swój UIButton w UIView. Ustaw te same ograniczenia, aby dołączyć go do krawędzi UIView.

  4. Ustaw 0 dla liczby wierszy UILabel. wprowadź opis obrazu tutaj

  5. Skonfiguruj punkty sprzedaży.

    @IBOutlet var button: UIButton!
    @IBOutlet var textOnTheButton: UILabel!
  1. Zdobądź długi, długi, długi tytuł.

    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 ustaw tytuł zarówno dla UILabel, jak i UIButton.

    override func viewDidLoad() {
        super.viewDidLoad()
        textOnTheButton.text = someTitle
        button.setTitle(someTitle, for: .normal)
        button.titleLabel?.numberOfLines = 0
    }
  1. Uruchom go, aby upewnić się, że rozmiar przycisku został zmieniony i można go nacisnąć.

wprowadź opis obrazu tutaj

Shalugin
źródło