iOS: изменение размера UIButton в соответствии с длиной текста


134

В конструкторе интерфейса, удерживая Command+, =вы измените размер кнопки, чтобы соответствовать ее тексту. Мне было интересно, если это было возможно сделать программно, прежде чем кнопка была добавлена ​​в представление.

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
Обратите внимание на этот очень старый вопрос: в настоящее время (2017 г.), просто установите ограничение как «больше или равно», и он делает все это. (Обратите внимание, ответ ниже Тэд.)
Толстяк

@Fattie Ограничение « больше или равно » недостаточно для совместимости с AutoLayout. В настоящее время (2017+) нужно либо обернуть кнопку внутри чего-то, что изменяет размер должным образом (ответ Bartłomiej Semańczyk), либо нужно создать подкласс UIButton, как в stackoverflow.com/a/50575588/1033581
Cœur

привет @ Cœur - есть много тонкостей для автоматического размещения. Возможно, вы думаете о немного другой ситуации? Однако, к счастью, вы совершенно не правы - просто попробуйте !! Просто установите UIButton на контроллере представления. очевидно установить горизонтальный и вертикальный центр. А затем добавьте ограничение ширины> = 100. он отлично работает в последней iOS.
Толстяк

1
(Для тех, кто плохо знаком с автолайтингом, здесь ........... просто TBC, вам вообще не нужно ничего добавлять, а UIButton (также UILabel и т. Д.) Автоматически изменяет ширину при изменении текста. Просто правильно ограничить положение H / V.)
Толстяк

@ Толстая демонстрация . В частности, во время выполнения он ведет себя так: github.com/Coeur/autolayoutButton/blob/master/buttonSelfSizing/…
Cœur

Ответы:


1

В Xcode 4.5 и выше это теперь может быть сделано с помощью ' Auto-layout / Constraints '.

Основные преимущества:

  1. Вам вообще не нужно программно устанавливать кадры!
  2. Если все сделано правильно, вам не нужно беспокоиться о сбросе кадров для изменения ориентации.
  3. Кроме того, изменения устройства не должны беспокоить вас (читайте, не нужно кодировать отдельно для разных размеров экрана).

Несколько недостатков:

  1. Не поддерживает обратную совместимость - работает только для iOS 6 и выше.
  2. Нужно ознакомиться (но сэкономит время позже).

Самое классное, что мы можем сосредоточиться на объявлении намерения, такого как:

  • Я хочу, чтобы эти две кнопки были одинаковой ширины; или
  • Мне нужно, чтобы это представление было вертикально центрировано и простиралось до максимума в 10 пунктов от края суперпредставления; или даже,
  • Я хочу, чтобы размер этой кнопки / ярлыка изменялся в соответствии с отображаемым ярлыком!

Вот простой учебник, чтобы познакомиться с автоматическим макетом.

Для более подробной информации .

Сначала это займет некоторое время, но, похоже, оно того стоит.


3
Я чуть не пропустил этот ответ - ему действительно нужно еще больше голосов. С помощью этого решения можно избежать встраивания имен и размеров шрифтов или вызовов только для iOS 7. Я просто установил ограничение на ту сторону, на которую я хотел бы расширить свою UIButton, и все.
Карл

Почти идеальное решение, к сожалению, оно не работает, если вы используете titleEdgeInsets: /
Золтан

130
Это плохой ответ - в нем подробно описываются достоинства самой AutoLayout, а не то, как AutoLayout может применяться в этой ситуации для решения проблемы автора.
mattsven

4
Мне было интересно, если это было возможно сделать программно, говорит ответ ...
Хуан Боэро

@JuanPabloBoeroAlvarez Ограничения автоматической разметки можно указать в Интерфейсном Разработчике и / или программно.
Слипп Д. Томпсон

130

В UIKit есть дополнения к классу NSString, чтобы получить от данного объекта NSString размер, который он примет при визуализации определенным шрифтом.

Документы были здесь . Теперь это здесь под устаревшим.

Короче, если вы идете:

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

... вы установили рамку кнопки на высоту и ширину отрисовываемой строки.

Возможно, вы захотите поэкспериментировать с некоторым буферным пространством вокруг этого CGSize, но вы начнете с правильного места.


4
Похоже, отступы по умолчанию для iOS - 12px, 8px.
Бен Лахман

19
sizeWithFont устарела в iOS 7.0. Используйте sizeWithAttributes: вместо этого
carmen_munich

6
Я настоятельно рекомендую больше не играть с фреймами, а только с ограничениями.
Гил Сэнд

@GilSand Есть ли способ использовать конструктор интерфейса для этого?
Huggie

Да, поиск в Google должен дать вам много полезных результатов
Гил Санд,

101

Способ сделать это в коде:

[button sizeToFit];

Если вы создаете подклассы и хотите добавить дополнительные правила, вы можете переопределить:

- (CGSize)sizeThatFits:(CGSize)size;

5
sizeToFit отлично работает на стандартных элементах управления, таких как UIButton или UILabel. Если вы делаете это на пользовательском UIView, вам нужно будет реализовать -(CGSize)sizeThatFits:(CGSize)size.
Кэмерон Лоуэлл Палмер

4
Это работает, только если вы поставили его после установки текста, в противном случае недостаточно информации о том, насколько большим он должен быть: [button setTitle:@"Your text here" forState:UIControlStateNormal]; [button sizeToFit]; Также, если вы обновите текст позже, не забудьте вызвать его еще раз.
Хуан Боэро

Убедитесь, что вы устанавливаете текст только таким образом: [myButton setTitle: @ "my title" forState: UIControlStateNormal]; Для меня это не сработало, пока я не повторил это утверждение.
Дариус Миляускас

7
sizeToFit()не уважает титульные вставки.
келин

32

Если ваша кнопка была создана с помощью Interface Builder, и вы меняете заголовок в коде, вы можете сделать это:

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

1
И вы можете сделать это, даже если ваша кнопка была сделана с кодом.
Маркус

22

Swift:

Здесь важно знать, когда вы будете вызывать .sizeToFit(), это должно быть после установки текста для отображения, чтобы он мог прочитать необходимый размер, если вы позже обновите текст, а затем вызовите его снова.

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

18

Чтобы сделать это с помощью autolayout, попробуйте установить ограничение переменной ширины:

Ограничение ширины

Вам также может понадобиться скорректировать свои результаты Content Hugging Priorityи Content Compression Resistance Priorityполучить нужные вам результаты.


UILabel полностью автоматически измеряет себя :

Эта UILabel просто устанавливается по центру на экране (только два ограничения, горизонтальное / вертикальное):

Изменяет ширину полностью автоматически:

Вам не нужно устанавливать какой - либо ширины или высоты - это полностью автоматический.

введите описание изображения здесь

введите описание изображения здесь

Обратите внимание, что маленькие желтые квадраты просто прикреплены («интервал» от нуля). Они автоматически перемещаются при изменении размера UILabel.

Добавление ограничения «> =» устанавливает минимальную ширину для UILabel:

введите описание изображения здесь


16

Если вы хотите изменить размер текста в отличие от кнопки, вы можете использовать ...

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
* AdjusttsFontSizeToFitWidth
Даниэль Банг

8

sizeToFit не работает правильно. вместо:

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

Вы также можете добавить contentInsetк кнопке:

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


Из документов Apple относительно sizeToFit: // вызывает sizeThatFits: с текущими границами представления и изменяет размер границ. Так что он работает правильно, вы просто должны установить его после того, как вы установите текст.
Маркус

5

Просто:

  1. Создать UIViewкак обертку с автоматическим макетом для просмотра вокруг.
  2. Положи UILabelв эту обертку. Добавьте ограничения, которые прикрепят ярлык к краям оболочки.
  3. Поместите UIButtonв свою обертку, затем просто добавьте те же ограничения, что и для UILabel.
  4. Наслаждайтесь кнопкой автоматического изменения размера вместе с текстом.

1
Это хорошо сочетается с доступностью?
Jasonszhao

4

Swift 4.2

Слава богу, это решено. После установки текста для кнопки вы можете получить intrinsicContentSize, который является естественным размером из UIView (официальный документ здесь ). Для UIButton вы можете использовать его, как показано ниже.

button.intrinsicContentSize.width

Для вашей информации я отрегулировал ширину, чтобы она выглядела правильно.

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

имитатор

UIB-кнопки с intrinsicContentSize

Источник: https://riptutorial.com/ios/example/16418/get-uibutton-s-size-strictly-based-on-its-text-and-font


1
Не могли бы вы дать немного больше контекста? Как ваша строка кода связана с кодом в вопросе? Не могли бы вы также обобщить, что делает код, который вы предлагаете? Только предоставление ссылки в качестве объяснения не является правильным форматом для этого сайта (см. Meta.stackexchange.com/a/8259/266197 по причинам), но если вы добавите некоторые пояснения и контекст, ваш ответ станет более ценным!
NOhs

3

У меня есть некоторые дополнительные потребности, связанные с этим постом, sizeToFitкоторый не решает. Мне нужно было держать кнопку по центру в исходном месте, независимо от текста. Я также никогда не хочу, чтобы кнопка была больше, чем ее оригинальный размер.

Итак, я размещаю макет кнопки для ее максимального размера, сохраняю этот размер в контроллере и использую следующий метод для изменения размера кнопки при изменении текста:

+ (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

Этот класс кнопок с автоматическим изменением размера текста (для Xamarin, но может быть переписан для другого языка)

[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

Почему-то func sizeToFit()у меня не работает. Моя установка - я использую кнопку внутри, UITableViewCellи я использую автоматическое расположение.

Что сработало для меня:

  1. получить ограничение ширины
  2. получить intrinsicContentSizeширину, потому что в соответствии с этим документом авто макет не знает о intrinsicContentSize. Установите ограничение intrinsicContentSizeширины на ширину

введите описание изображения здесь

Вот два названия из Debug View Hierachry введите описание изображения здесь

введите описание изображения здесь


0

Честно говоря, я думаю, что это действительно позор, что в раскадровке нет простого флажка, чтобы сказать, что вы хотите изменить размер кнопок, чтобы разместить текст. Ну ... что угодно.

Вот простейшее решение с использованием раскадровки.

  1. Разместите UIView и наложите на него ограничения. Пример: введите описание изображения здесь
  2. Поместите UILabel внутри UIView. Установите ограничения, чтобы прикрепить его к краям UIView.

  3. Поместите свой UIButton внутри UIView. Установите те же ограничения, чтобы прикрепить его к краям UIView.

  4. Установите 0 для количества строк UILabel. введите описание изображения здесь

  5. Настройте торговые точки.

    @IBOutlet var button: UIButton!
    @IBOutlet var textOnTheButton: UILabel!
  1. Получить длинный, длинный, длинный заголовок.

    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. На viewDidLoad установите заголовок как для UILabel, так и для UIButton.

    override func viewDidLoad() {
        super.viewDidLoad()
        textOnTheButton.text = someTitle
        button.setTitle(someTitle, for: .normal)
        button.titleLabel?.numberOfLines = 0
    }
  1. Запустите его, чтобы убедиться, что размер кнопки изменен и ее можно нажать.

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.