Як я можу отримати поточну ширину та висоту подання при використанні обмежень для автоматичного вимикання?


108

Я не кажу про властивість кадру, тому що з цього ви можете отримати лише розмір перегляду в xib. Я говорю про те, коли вигляд змінюється через його обмеження (можливо, після обертання чи у відповідь на подію). Чи є спосіб отримати його поточну ширину та висоту?

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

Чому це мені так важко. АРГ!


Я б міг подумати, що межі виду в будь-який конкретний час утримували його поточну ширину та висоту. Ось що коригується в процесі компонування.
Філіп Міллс

Хм, я ніколи не думав перевіряти межі. Я зараз це зроблю.
Юф

Відповіді:


246

Відповідь така [view layoutIfNeeded] .

Ось чому:

Ви все одно отримуєте поточну ширину та висоту подання, оглядаючи view.bounds.size.widthта view.bounds.size.height(або кадр, що еквівалентно, якщо ви не граєте зview.transform ).

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

Як ви попросите автоматичний макет оновити макет? Дзвінок[view setNeedsLayout] якщо ви хочете, щоб автоматичний макет оновив макет у наступному ході циклу запуску.

Однак, якщо ви хочете, щоб він оновлював макет негайно, щоб ви могли негайно отримати доступ до нового значення меж пізніше в межах вашої поточної функції, або в інший момент до повороту циклу запуску, тоді вам потрібно зателефонувати [view setNeedsLayout]і [view layoutIfNeeded].

Ви задали друге запитання: "як я можу змінити обмеження по висоті / ширині, якщо безпосередньо не маю на нього посилання?".

Якщо ви створюєте обмеження в IB, найкращим рішенням буде створити IBOutlet у контролері перегляду чи у вашому представленні, щоб у вас було пряме посилання на нього. Якщо ви створили обмеження в коді, вам слід затриматися на посиланні у внутрішньому слабкому властивості під час його створення. Якщо хтось інший створив обмеження, то вам потрібно знайти його, вивчивши вивчення властивості view.constraints на поданні, можливо, і всю ієрархію перегляду, та застосувавши логіку, яка знаходить вирішальний NSLayoutConstraint. Це, мабуть, неправильний шлях, оскільки він також фактично вимагає від вас визначити, яке саме обмеження визначало розмір меж, коли на це питання не гарантується проста відповідь. Кінцеве значення меж може бути рішенням дуже складної системи з декількома обмеженнями,


16
Відмінний відповідь і добре пояснили, теж. Врятували мене через годину-дві витягування волосся.
Бен Крігер

2
layoutIfNeededчудово, і зробить кадр негайно доступним. Однак це також змусить надавати обмеження для всіх поглядів у під-деревах виду, на який він викликаний. Якщо ви, наприклад, додаєте перегляди програмно і закликаєте їх layoutIfNeededусіх у своєму рекурсивному режимі, ви можете виявити, що ваша ієрархія перегляду відображається дуже повільно. (Я це навчився важко) Як зазначено у чудовій відповіді, "setNeedsLayout" є більш ефективним і зробить кадр доступним при наступному пропуску макета, що в ідеалі відбувається приблизно через 1/60-ту секунду.
шмім

1
Я знаю, що це старе, але де ти називаєш цей метод? В initWithCoder?
MayNotBe

4
Навіщо змушувати макет просто отримати межі? Це здається неефективним, а зі складним інтерфейсом це може бути дорогим. Натомість отримайте доступ до останніх меж з viewDidLayoutSubviews або навіть viewWillLayoutSubviews і дозвольте какао обробляти власні терміни компонування. Установіть властивість, якщо вам потрібно отримати доступ до значення або прапор, щоб уникнути декількох дзвінків, якщо це проблема.
усмішкаБот

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

8

У мене була аналогічна проблема, коли мені потрібно було додати верхню та нижню межі до параметра, UITableViewщо змінює розмір, виходячи із встановлених обмежень у програмі UIStoryboard. Я зміг отримати доступ до оновлених обмежень за допомогою - (void)viewDidLayoutSubviews. Це корисно для того, що вам не потрібно підкласифікувати подання та змінювати його метод компонування.

/*** SET TOP AND BOTTOM BORDERS ON TABLE VIEW ***/
- (void)addBorders
{
    CALayer *topBorder           = [CALayer layer];
    topBorder.frame              = CGRectMake(0.0f, self.tableView.frame.origin.y, 320.0f, 0.5f);
    topBorder.backgroundColor    = [UIColor redColor].CGColor;

    CALayer *bottomBorder        = [CALayer layer];
    bottomBorder.frame           = CGRectMake(0.0f, (self.tableView.frame.origin.y + self.tableView.frame.size.height), 320.0f, 0.5f);
    bottomBorder.backgroundColor = [UIColor redColor].CGColor;

    [self.view.layer addSublayer:topBorder];
    [self.view.layer addSublayer:bottomBorder];
}

/*** GET AUTORESIZED FRAME DIMENSIONS ***/
- (void)viewDidLayoutSubviews{
    [self addBorders];
}

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


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

Прокоментуйте кілька років із запізненням ... Ви також повинні закликати цей метод на надкласовій клавіші, коли це буде перекреслено перед будь-чим іншим:[super viewDidLayoutSubviews];
Алехандро Іван

5

Для тих, хто все ще може зіткнутися з подібними проблемами, особливо з TableviewCell.

Просто перекрийте метод:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

У разі UITableViewCell або UICollectionViewCell створіть підклас комірки та замініть той же метод:

-(void)layoutSubviews
{
//your code here like drawing a shadow
}

це єдиний спосіб, коли я міг отримати реальний остаточний розмір моєї точки зору
Бенуа Джадінон,

Це був єдиний спосіб, коли я зміг отримати остаточний розмір для будь-якого з моїх обмежених поглядів, тому що, перш ніж я намагався запустити їх в режим пробудженняFromNib, який не дав часу підглядам фактично змінити розмір першим. Дякую!
Азін Мерноош

3

Використовувати -(void)viewWillAppear:(BOOL)animatedта телефонувати [self.view layoutIfNeeded];- Це працює, я спробував.

тому що якщо ви -(void)viewDidLayoutSubviewsйого використовуєте, він буде працювати безумовно, але цей метод викликається кожен раз, коли ваш інтерфейс вимагає оновлення / зміни. Яким буде важко керувати. Програмний ключ - це те, що ви використовуєте змінну bool, щоб уникнути такого циклу дзвінків. краще використовувати viewWillAppear. Пам'ятайте viewWillAppearтакож буде викликано, якщо перегляд буде завантажений знову (без перерозподілу).


2

Кадр все ще діє. Зрештою, представлення використовує властивість кадру, щоб викласти себе. Він обчислює цей кадр, виходячи з усіх обмежень. Обмеження використовуються лише для початкового макета (і будь-який час layoutSubviews викликається у представленні, як після обертання). Після цього інформація про положення знаходиться у властивості кадру. Або ти бачиш інакше?


1
Я бачу інакше. Значення кадру не змінюються навіть після прямого зміни обмежень ширини / висоти (зміною їх констант. У мене є IBOutlet для них у xib)
юф

Моя проблема є унікальною, оскільки я змінюю обмеження, тоді мені потрібно використовувати кадр у тій же функції. Виклик setNeedsLayout не оновлює його відразу. Я думаю, що мені спочатку потрібно дочекатися макета, а потім скористатися рамкою. Друге моє запитання: як я можу змінити обмеження по висоті / ширині, якщо я не маю посилання на нього безпосередньо?
Юф

1
Ви повинні dispatch_async тоді, і це дозволить затримати ваш код до наступного кадру. Що стосується другої частини ... я не знаю ... вам доведеться повторити всі обмеження і шукати, яке з них застосовується ... IBOutlets набагато простіше.
borrrden

Що стосується IBOutlets, але я хочу написати функцію в категорії для UIView, з якою у мене не буде ІБ.
Юф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.