Autolayout - внутрішній розмір UIButton не включає вставки заголовків


196

Якщо у мене є UIButton, організований за допомогою автоматичного вимикання, його розмір добре регулюється відповідно до його вмісту.

Якщо я встановив зображення як " button.image, то, здається, внутрішній розмір знову спричиняє це.

Однак якщо я налаштував titleEdgeInsetsкнопку, макет цього не враховує, а замість цього обрізає назву кнопки.

Як я можу забезпечити, щоб внутрішня ширина кнопки враховувала вкладку?

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

Редагувати:

Я використовую наступне:

[self.backButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 5, 0, 0)];

Мета - додати деякий відрив між зображенням і текстом.


3
Ви подали це як радар? Це, безумовно, виявляється помилкою в внутрішніх розрахунках розміру UIButton.
Ryan Poolos

1
Я був готовий подати радар, але це насправді є очікуваною поведінкою. Це задокументовано у властивостях UIButton * EdgeInsets : " Введені вами вставки застосовуються до прямокутника заголовка після того, як цей прямокутник був розміщений під текст кнопки. Таким чином, позитивні значення вставки можуть насправді вирізати текст заголовка. [...] кнопка не використовує це властивість для визначення intrinsicContentSize та sizeThatFits: "
Гійом Алгіс

7
@GuillaumeAlgis Я б заперечував, що хоча це констатується поведінкою, це зовсім не те, що можна було б очікувати, що станеться під час використання автоматичного вимикання. Я подав помилку і закликаю інших також подати один із них.
пам’ятки

Якщо ви можете посилання на радіолокаційну помилку тут, чи можемо ми натиснути на неї та поставити +1?
gprasant

1
з titleEdgeInsetдокументації: The insets you specify are applied to the title rectangle after that rectangle has been sized to fit the button’s text. Thus, positive inset values may actually clip the title text. Отже, додавши вставку, ви змушуєте кнопку точно вирізати текст
Marco Pappalardo

Відповіді:


192

Ви можете вирішити це, не змінюючи жодних методів або встановлюючи довільне обмеження по ширині. Ви можете це зробити в Interface Builder наступним чином.

  • Внутрішня ширина кнопки походить від ширини заголовка плюс ширини піктограми плюс лівого та правого крайового вставки вмісту .

  • Якщо на кнопці є зображення і текст, вони зосереджені як група, без прокладки між ними.

  • Якщо ви додаєте ліву вставку вмісту, вона обчислюється відносно тексту, а не тексту + піктограми.

  • Якщо встановити негативну ліву вставку, зображення витягується зліва, але загальна ширина кнопки не впливає.

  • Якщо встановити негативну ліву вставку зображення, фактичний макет використовує половину цього значення. Отже, щоб отримати ліву вставку -20 балів, ви повинні використовувати значення лівої вставки -40 балів у програмі Interface Builder.

Таким чином, ви надаєте достатньо великий лівий вкладений вміст, щоб створити простір для обох бажаних лівих вкладок і для внутрішньої прокладки між піктограмою та текстом, а потім перемістіть піктограму вліво, подвоївши кількість необхідної накладки між піктограмою та текстом. Результат - кнопка з рівними лівими та правими вставками вмісту, а також пара тексту та значків, по центру якої є група, із певним накладом між ними.

Деякі приклади значень:

// Produces a button with the layout:
// |-20-icon-10-text-20-|
// AutoLayout intrinsic width works as you'd desire.
button.contentEdgeInsets = UIEdgeInsetsMake(10, 30, 10, 20)
button.imageEdgeInsets = UIEdgeInsetsMake(0, -20, 0, 0)

чому власне макет використовує половину від’ємного лівого значення вставки ?? Я зіткнувся з тією ж проблемою!
Тоні Лін

1
Чудово, що є рішення, але я сподіваюся, що це не використовується для виправдання дивної поведінки UIButton.
funct7

205

Ви можете дозволити це працювати в Interface Builder (без написання будь-якого коду), використовуючи поєднання негативних та позитивних вкладів заголовка та вмісту.

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

Оновлення : Xcode 7 має помилку, в яку не можна вводити негативні значення уRight поле Inset, але ви можете використовувати ступінчасте управління поруч із ним, щоб зменшити значення. (Спасибі Стюарт)

Це додасть 8pt проміжку між зображенням і заголовком і збільшить внутрішню ширину кнопки на стільки ж. Подобається це:

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


2
Використовуючи contentEdgeInsets (який не є помилкою), щоб дозволити автовідклад збільшувати ширину кнопки. І перемістіть мітку на порожнє місце праворуч. Розумний спосіб вирішити помилку вставки заголовка.
угор

7
Цей трюк більше не працює. Побудова інтерфейсу більше не приймає негативні значення в Rightполі.
Йоріс Манс

7
@JorisMans Ви не можете вводити негативні значення, але це працювало для мене, використовуючи ступінчасте управління праворуч від текстового поля, щоб перейти до необхідного негативного значення ... перейдіть до цифри!
Стюарт

3
Це має бути першою відповіддю, чому це тут? Я спробував інші 5, перш ніж дізнатися це ...
Лорд Золт

2
Я зробив право на вміст 16, щоб
зосередити

96

Чому б не замінити intrinsicContentSizeметод на UIView? Наприклад:

- (CGSize) intrinsicContentSize
{
    CGSize s = [super intrinsicContentSize];

    return CGSizeMake(s.width + self.titleEdgeInsets.left + self.titleEdgeInsets.right,
                      s.height + self.titleEdgeInsets.top + self.titleEdgeInsets.bottom);
}

Це повинно сказати системі автоматичного вимикання, що вона повинна збільшити розмір кнопки, щоб мати змогу вводити вставки та показувати повний текст. Я не за власним комп’ютером, тому не перевіряв цього.


1
Наскільки я не знаю, кнопки не слід перекривати. Проблема полягає в тому, що кожен тип кнопки реалізований різним підкласом.
Султан

2
intrinsicContentSizeце метод на UIView, а не UIButton, тому ви не заплутаєтесь у жодних методах UIButton. Apple не вважає, що це проблема: "Переосмислення цього методу дозволяє користувальницькому перегляду повідомляти системі компонування, якого розміру він хотів би базуватись на його вмісті". А ОП нічого не сказала про різні кнопки, лише одну.
Маартен

1
Це безумовно працює і є рішенням, з яким я пішов. intrinsicContentSizeце дійсно метод на UIView, а UIButton - це підклас UIView, тому, звичайно, ви можете перекрити цей метод; нічого в документах Apple не говорить про те, що не слід. Просто створіть підклас UIButton за допомогою методу Maarten, який перекрито, та змініть свій UIButton в Interface Builder на тип YourUIButtonSubclass, і він буде працювати ідеально.
n8tr

37
Мені здається, intrinsicContentSizeщо UIButton повинен додати у titleEdgeInsets, я збираюся подати помилку в Apple.
progrmr

6
Я згоден, те ж саме для imageEdgeInsets.
Рікардо Санчес-Саес

87

Ви не вказали, як ви встановлюєте вставки, тому я гадаю, що ви використовуєте titleEdgeInsets, тому що я бачу той же ефект, який ви отримуєте. Якщо я використовую contentEdgeInsets, замість цього він працює належним чином.

- (IBAction)ChangeTitle:(UIButton *)sender {
    self.button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);
    [self.button setTitle:@"Long Long Title" forState:UIControlStateNormal];
}

Я справді використовую titleEdgeInsets. Мені потрібно віддалити назву від зображення, а не зображення від краю кнопки. Можливо, я просто повинен використовувати зображення з деякими накладками в ньому? Здається, хакі.
Бен Пакард

Це чудово працює в поєднанні з автоматичним розгортанням, дякую!
Cal S

3
Це краще рішення, оскільки воно робить саме те, що ви хочете, не торкаючись intrinsicContentSize.
RyJ

28
Це НЕ відповідає на запитання під час використання зображення та потребує коригування вставки між зображенням та заголовком!
Броді Робертсон

24

А для Свіфт працював так:

extension UIButton {
    override open var intrinsicContentSize: CGSize {
        let intrinsicContentSize = super.intrinsicContentSize

        let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
        let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom

        return CGSize(width: adjustedWidth, height: adjustedHeight)
    }
}

Love U Swift


1
Незважаючи на те, що вам цього не передбачається, краще підкласи в цьому випадку, оскільки документи Apple чітко зазначають, що внутрішній розмір не включає титул титульного розміру в його обчислення, і, використовуючи розширення, ви порушуєте не лише очікування Apple, але й усі інші розробники, які читають док.
Сирени

18

Цей потік трохи старий, але я просто натрапив на це сам і зміг його вирішити, використовуючи негативну вставку. Наприклад, замініть потрібні значення прокладки тут:

UIButton* myButton = [[UIButton alloc] init];
// setup some autolayout constraints here
myButton.titleEdgeInsets = UIEdgeInsetsMake(-desiredBottomPadding,
                                            -desiredRightPadding,
                                            -desiredTopPadding,
                                            -desiredLeftPadding);

У поєднанні з правильними обмеженнями для автоматичного розміщення, ви закінчуєте кнопку автоматичного зміни розміру, яка містить зображення та текст! Видно нижче та desiredLeftPaddingвстановлено 10.

Кнопка із зображенням та коротким текстом

Кнопка із зображенням і довгим текстом

Ви можете бачити, що фактичний кадр кнопки не охоплює мітку (оскільки мітка зміщена на 10 точок праворуч, поза межами меж), але ми досягли 10 точок прокладки між текстом і малюнком.


1
Це рішення, яке я використав, оскільки воно не потребує підкласифікації. Не буде працювати, якщо кнопка має тло, але це зазвичай не проблема з iOS 7
Жозе Мануель Санчес

Це буде працювати з фоновим зображенням, якщо ви також встановите зміщення змісту кнопки (додатне значення> = вставка заголовка).
Бен Флінн

9

Для Swift 3 на основі відповіді pegpeg :

extension UIButton {

    override open var intrinsicContentSize: CGSize {

        let intrinsicContentSize = super.intrinsicContentSize

        let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
        let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom

        return CGSize(width: adjustedWidth, height: adjustedHeight)

    }

}

Привіт. Я хочу використовувати спеціальну кнопку розширення в інтерфейсі. plz help
kemdo

6

Все вище не працювало для iOS 9+ , що я робив:

  • Додайте обмеження по ширині (для мінімальної ширини, якщо на кнопці немає тексту. Кнопка автоматично масштабується, якщо текст наданий)
  • встановити відношення до великого чи рівного

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

Тепер для додавання рамки навколо кнопки просто скористайтеся методом:

button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);

Чому ні? Він автоматично масштабується із вмістом, просто потрібно встановити мінімальну ширину (яка може бути меншою, ніж текст, що відображається)
Оритм

Тому що ви визначаєте мінімальну ширину. Вся ідея авторозмітки - це зроблено без встановлення явної (мінімальної) ширини.
Joris Mans

Це не про ширину, ви можете встановити ширину на 1, якщо вам зручніше, але для авторозмітки потрібно знати, що ширина може бути рівною або більшою . Я оновив свою відповідь
Оритм

Ви взагалі не потребуєте обмеження ширини, contentEdgeInset є ключовим, автоматичний макет потім використовує його для внутрішнього розміру вмісту.
Кріс Коновер

5

Я хотів додати пробіл у 5 кпт між моєю піктограмою UIButton та міткою. Ось як я цього досяг:

UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeCustom];
// more button config etc
infoButton.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 5);
infoButton.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, -5);

Те, як contentEdgeInsets, titleEdgeInsets і imageEdgeInsets співвідносяться один з одним, вимагає трохи віддачі та прийому від кожного вставки. Тож якщо ви додасте кілька вводів зліва від заголовка, ви повинні додати негативну вставку праворуч та забезпечити трохи більше місця (через позитивну вставку) праворуч на вміст.

Додавши правильну вставку вмісту, яка відповідає зміні вставки заголовка, мій текст не виходить за межі кнопки.


3

Опція також доступна у програмі для створення інтерфейсів. Див. Вкладку. Я встановив ліворуч і праворуч на 3. Працює як шарм.

Скріншот розробника інтерфейсу


1
Так, як пояснюється в цій відповіді , причиною цього є те, що ви налаштовуєте тут Edge: Content замість Edge: Title або Edge: Image .
smileyborg

1

Я використовую рішення - додати обмеження на ширину кнопки. Потім десь під час ініціалізації після встановлення тексту оновіть обмеження ширини так:

self.buttonWidthConstraint.constant = self.shareButton.intrinsicContentSize.width + 8;

Де 8 - це все, що є вашою вставкою.


Що таке кнопкаWidthConstraint?
Олексій Голіков


1
Це не чудове рішення, адже якщо зміна розміру кнопки змісту власного вмісту вам потрібно буде вручну оновити constantобмеження на нове значення ... і знаючи, коли змінити розмір кнопки власного вмісту важко без підкласифікації кнопки.
усміхборг

Аюп. Я більше не використовую цей метод. Здивувавшись, це гідне голосування проти, але ¯_ (ツ) _ / ¯
Боб Сприн

Виклик на " setNeedsUpdateConstraintsможна" зробити вручну після оновлення заголовка або зображення кнопки. Потім ви можете перекрити updateConstraintsта перерахувати buttonWidthConstraintконстанту звідти. Це не обов'язково найкращий підхід, але він працює досить добре для мене. YMMV;)
Олів'є

1

виклик sizeToFit () гарантує, що contentEdgeInset набуває чинності

extension UIButton {

   open override func draw(_ rect: CGRect) { 
       self.contentEdgeInsets = UIEdgeInsets(top: 10, left: 40, bottom: 10, right: 40)
       self.sizeToFit()
   }
}

Питання проtitleEdgeInsets
androidguy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.