Проблема с AutoLayout на UITableViewCell
У меня возникли проблемы с автозапуском в проекте xcode 5
. Я использую контроллер открытого вида внутри с навигационным контроллером. У меня есть MKMapView
в верхней половине и UITableView
в нижней половине. Я использую storyboards
и настроил прототип UITableViewCell
, но я добавляю ограничения через код. Я дважды проверял каждый элемент управления в прототипе и не вижу никаких ограничений, настроенных там. Моя проблема возникает, когда я добавляю ограничения для UITableViewCell
. У меня есть следующий код в ячейках:
-(void)updateConstraints {
[super updateConstraints];
//first remove old constraints
[self removeConstraints:self.constraints];
[self.nameLabel removeConstraints:self.nameLabel.constraints];
[self.addressLabel removeConstraints:self.nameLabel.constraints];
[self.rentableSquareFeetLabel removeConstraints:self.rentableSquareFeetLabel.constraints];
[self.lastSaleAmountLabel removeConstraints:self.lastSaleAmountLabel.constraints];
[self.lastSaleDateLabel removeConstraints:self.lastSaleAmountLabel.constraints];
[self.thumbnailImageView removeConstraints:self.thumbnailImageView.constraints];
//then set up constraints
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_thumbnailImageView, _nameLabel, _rentableSquareFeetLabel, _lastSaleAmountLabel, _addressLabel, _lastSaleDateLabel);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_thumbnailImageView(60)]-[_nameLabel(<=200)]-(>=8)-[_rentableSquareFeetLabel]-(>=8)-[_lastSaleAmountLabel]|" options:0 metrics:nil views:viewsDictionary]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_nameLabel]-(-4)-[_addressLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:viewsDictionary]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_lastSaleAmountLabel]-(-4)-[_lastSaleDateLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:viewsDictionary]];
}
Я получаю следующее в консоли отладки. Исключение запускается первой линией addConstraints. Если я просто продолжу их, то в конечном итоге все будет выглядеть так, как должно быть, так как выглядит так, как xcode выбирает разбить правильное ограничение:
2013-09-25 15:07:14.169 PECProperties[32381:a0b] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) (
"<NSIBPrototypingLayoutConstraint:0x9d56c70 'IB auto generated at build time for view with fixed frame' H:|-(0)-[UIImageView:0x9d558f0](LTR) (Names: '|':UITableViewCellContentView:0x9d55620 )>",
"<NSIBPrototypingLayoutConstraint:0x9d56d20 'IB auto generated at build time for view with fixed frame' H:[UIImageView:0x9d558f0(60)]>",
"<NSIBPrototypingLayoutConstraint:0x9d56d80 'IB auto generated at build time for view with fixed frame' H:|-(78)-[UILabel:0x9d559e0](LTR) (Names: '|':UITableViewCellContentView:0x9d55620 )>",
"<NSLayoutConstraint:0x9d53830 H:[UIImageView:0x9d558f0]-(NSSpace(8))-[UILabel:0x9d559e0]>" )
Will attempt to recover by breaking constraint <NSIBPrototypingLayoutConstraint:0x9d56d80 'IB auto generated at build time for view with fixed frame' H:|-(78)-[UILabel:0x9d559e0](LTR) (Names: '|':UITableViewCellContentView:0x9d55620 )>
Break on objc_exception_throw to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Третий NSIBPrototypingLayoutConstraint показывает 78 точек между краем представления и меткой. Вот где прототип позиционируется грубо (и если я его перемещаю в прототипе, я вижу изменение ограничения в консоли отладки), но это противоречит моему собственному ограничению "стандартного" расстояния между представлением изображения и меткой,
Я попытался установить translatesAutoresizingMaskIntoConstraints=NO
в контроллере представления cellForRowAtIndexPath
, но это, похоже, не помогает. Как я могу исправить макет?
Ответы
Ответ 1
Несколько вещей, которые можно здесь затронуть:
-
Ограничения NSIBPrototypingLayoutConstraint
, с которыми вы работаете (и которые вызывают исключения), автоматически генерируются с помощью Interface Builder, чтобы сделать раскладку раскадровки или XIB-представление недвусмысленным. Это довольно сложно сделать, но оно автоматически добавляет минимальные ограничения, необходимые для полного определения положения и размера каждого неоднозначного представления. Это изменение от Xcode 4, потому что в Xcode 4 вы не могли бы иметь двусмысленные макеты в Interface Builder. С Xcode 5 и более поздними версиями вы можете, однако, IB будет автоматически генерировать эти ограничения для вас, если ваш макет неоднозначен во время компиляции.
Способ устранения этой проблемы заключается в том, чтобы добавить минимальные требуемые ограничения в Interface Builder, чтобы полностью определить каждую позицию и размер представления, затем выберите каждое из этих нежелательных ограничений, перейдите в инспектор атрибутов правой боковой панели и установите флажок рядом с Заполнитель - Удалить во время сборки.
![Screenshot of the constraint Placeholder checkbox in Xcode 6.]()
Этот флажок не только удаляет ограничение, которое вы добавили, но, самое главное, это предотвратит его автоматическое создание ограничения IB. (Как вы можете себе представить, это довольно утомительно, когда у вас есть несколько просмотров в IB и вы хотите управлять всеми вашими ограничениями в коде. По этой причине вам может понадобиться избежать использования IB полностью для иерархий представлений, в которых вы намерены реализовать Auto Макет программно.)
В чем разница между ограничением Placeholder и неустановленным ограничением? Здесь слайд из моего Адаптивный автоматический макет (видео) (PDF слайды), сравнивая два:
![Comparison between Placeholder constraints and Uninstalled constraints.]()
-
В updateConstraints
вы не хотите удалять ограничения и повторно добавлять их, как у вас там. Почему нет? По сути, это ужасно для производительности, и я подтвердил инженерам Apple, что это не очень хорошая идея. См. <Вопрос/ответ который я разместил здесь, для получения более подробной информации, а также ответа . Чтобы предотвратить добавление ограничений более одного раза, используйте флаг boolean (например, hasSetupConstraints
), который вы установили в YES, когда вы впервые установили свои ограничения, и если updateConstraints
вызывается снова, вы можете сразу вернуться если у вас нет новых ограничений для добавления. См. этот вопрос для дальнейшего обсуждения.
-
Код, который вы используете для удаления ограничений, может работать не полностью. Это связано с тем, что [view removeConstraints:view.constraints]
будет удалять только те ограничения, которые были добавлены в view
, - помните, что ограничения могут быть добавлены в любой общий просмотр представлений, которые они ограничивают, и ограничения, добавленные в view
, могут быть не единственными которые влияют на расположение view
! Если вам нужно удалить несколько ограничений, вы должны сохранить ссылку на каждое из этих ограничений в свойстве (например, свойство NSArray, содержащее экземпляры NSLayoutConstraint), а затем деактивировать/удалить эти ограничения с помощью API в NSLayoutConstraint или библиотека с открытым исходным кодом PureLayout. Вы должны только деактивировать/удалить как можно меньше ограничений, потому что это дорого стоит вычислить. С другой стороны, изменение constant
любого ограничения очень эффективно и рекомендуется, и вам не нужно удалять или повторно добавлять ограничение для этого.