Могу ли я использовать setFrame и autolayout в одном и том же виде?

Я хочу добавить отступ ко всем своим кнопкам, поэтому я создал подкласс UIButton, и среди других изменений я хотел добавить фиксированное заполнение с помощью метода setFrame. Все заработало, кроме setFrame. Я проверил все и обнаружил, что если я сниму отметку с «using AutoLayout» в этом представлении, то я могу использовать setFrame, и это сработает. Это можно обойти? Я действительно хочу использовать автоматическое размещение, потому что это помогает сделать приложение красивым как на iphone 5, так и на более ранних устройствах. Но я также хотел бы использовать setFrame в своем подклассе, чтобы немного облегчить себе жизнь.

Подводя итог, мой вопрос: могу ли я использовать автоматическое размещение, а также программно настроить фрейм UIView?


person SirRupertIII    schedule 01.11.2012    source источник


Ответы (5)


Да, это можно сделать.

Если вы устанавливаете translatesAutoresizingMaskIntoConstraints = YES для представления, то вызовы setFrame: автоматически переводятся во время выполнения в ограничения макета на основе текущего autoresizingMask представления. Это позволяет смешивать макет на основе кадров с макетом на основе ограничений.

Например, вы можете использовать Auto Layout для определения макета всех подвидов представления, но по-прежнему вызывать setFrame:, чтобы установить размер и положение самого представления. С вашей точки зрения, вы делаете макет, сочетая автоматический макет и прямую манипуляцию фреймом. Но на самом деле система использует ограничения, чтобы справиться со всем.

Однако есть одно большое предостережение по поводу использования translatesAutoresizingMaskIntoConstraints.

Когда вы это сделаете, вам все равно нужно убедиться, что эти автоматические ограничения могут быть удовлетворены остальными вашими ограничениями.

Так, например, предположим, что уже есть ограничения, которые определяют размер и положение вашего представления, а затем вы также устанавливаете translatesAutoresizingMaskIntoConstraints = YES и вызываете setFrame:. Вызов setFrame: создаст новые ограничения для представления, которые, вероятно, будут конфликтовать с уже существующими ограничениями.

(На самом деле, эта ошибка случается часто. Если вы когда-либо видите сообщение журнала с жалобой на конфликтующие ограничения, и одним из этих ограничений является NSAutoresizingMaskLayoutConstraint, то вы видите конфликт с автоматическим ограничением. Это простая ошибка, поскольку translatesAutoresizingMaskIntoConstraints = YES является значением по умолчанию, поэтому, если вы настраиваете ограничения в коде, вам нужно не забыть выключить его, если вы не хотите эти автоматические ограничения.)

Напротив, предположим снова, что уже существуют ограничения, которые определяют размер и положение вашего представления, но затем вы устанавливаете translatesAutoresizingMaskIntoConstraints = NO перед вызовом setFrame:. В этом случае ваши setFrame: вызовы не будут создавать новых ограничений, поэтому не будет конфликта между отдельными ограничениями. Однако в этом случае все еще существует «конфликт» между ограничениями и установленным вами значением кадра. При следующем вызове Auto Layout он увидит уже существующие ограничения в представлении, вычислит требуемое значение кадра и сам установит для кадра требуемое значение, сократив значение, которое вы установили вручную.

Дополнительные сведения см. В разделе «Принятие автоматического макета» в Cocoa от Apple. Руководство по автоматической компоновке.

person algal    schedule 24.11.2012
comment
Это работает, но я вижу много предупреждений в журналах. Кто-нибудь может предложить, как правильно использовать этот подход? Или мне просто игнорировать предупреждения? Спасибо - person Husyn; 11.10.2014
comment
Если вы видите предупреждения о невыполнимых ограничениях, они, вероятно, имеют смысл. Если вы видите их при работе с translatedAutoresizingMaskIntoConstraints=true в представлении, то, вероятно, происходит то, что у вас уже есть некоторые ограничения (возможно, установленные в IB), которые пытаются управлять фреймом этого представления. Эти старые ограничения на фрейм взаимодействуют с теми, которые автоматически сгенерированы из маски автоизменения размеров. В этом случае ответ - удалить эти старые ограничения. Если вы добавили их в IB только для того, чтобы IB отключился во время разработки, вы можете сделать их заполнителями, чтобы их потерять. - person algal; 14.10.2014
comment
@algal как можно избавиться от старых ограничений? И сделайте так, чтобы подпредставление просто слушало setFrame: values, чтобы два из них не конфликтовали. - person Peter; 18.06.2016
comment
Это все еще работает на iOS 10? Я пытаюсь сделать это для дочернего VC в пользовательском контейнере VC, но он не соблюдает установленный мной фрейм. - person bartzy; 10.10.2016
comment
Я заметил, что если вы делаете это, во многих случаях вам может понадобиться вывести CGRect фреймов из фреймов других представлений, которые используют автоматический макет. В этой ситуации вам необходимо выполнить расчеты фрейма после того, как другие представления завершили свой этап автоматической компоновки. В представлении контроллер viewDidLayoutSubviews - это место для этого. В пользовательском представлении следует использовать layoutSubviews. В обоих случаях вы должны поместить вычисления кадра после вызова super. Возможно, стоит добавить это к ответу, но я все равно надеюсь, что это будет полезный комментарий! - person Benjohn; 11.01.2017

Самым простым для меня оказалось удаление представления, которое я хотел переместить / изменить размер, из его супервизора, установить его frame, а затем добавить его обратно. Например, возьмите пользовательский UILabel в подклассе UITableViewCell:

[cell.myLabel removeFromSuperview];
cell.myLabel.frame = someFrameIGenerated;
[cell.contentView addSubview:cell.myLabel];
person Andrew    schedule 30.10.2013
comment
Без проблем. Хотя я чувствую себя обязанным вам сказать, что в конечном итоге я избавился от этого и фактически научился использовать ограничения в IB. Я понятия не имел, что они существуют, и теперь создание сложных макетов кажется гораздо более разумным. - person Andrew; 05.12.2013
comment
Да, я пробовал это, но не смог получить ограничения при работе с представлением заголовка таблицы. Возможно, мне стоит попробовать еще раз. - person Marky; 12.12.2013
comment
Делая это, вы устраняете ограничения, которые могут испортить другие вещи. Будь утомлен. - person donkey; 08.03.2016

Что-то, что сработало для меня, - это просто добавление IBOutlet для моих ограничений к контроллеру представления, а затем изменение свойства constant на выходе.

Например, чтобы изменить начало координат x представления, установите выход из ведущего ограничения этого представления на ваш контроллер. Другими словами, создайте розетку @IBOutlet var labelXOrigin: NSLayoutConstraint!. Затем, когда вы хотите отрегулировать положение x, просто сделайте что-нибудь вроде

self.labelXOrigin.constant = 20.0 // Or whatever origin you want to set.
person Nick Yap    schedule 20.03.2016

Лучший способ установить фрейм дополнительного представления - это метод viewWillLayoutSubviews в проектах с автоматическим размещением.

установить свойство translatedAutoresizingMaskIntoConstraints = true, это будет работать после того, как представление действительно появилось или вкладка на кнопке, которую нужно установить, сразу после того, как представление действительно появилось.

person Ravi Kumar    schedule 14.06.2016

Если ваше приложение не поддерживает ориентацию нескольких устройств.

В viewDidLoad установите рамку представления, размер которой вы хотите изменить.

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [aView setFrame:CGRectMake(15, 15, 100, 100)];
}

Затем в viewDidLayoutSubviews:

-(void)viewDidLayoutSubviews
{
    [self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
}

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

person EFE    schedule 11.09.2015