IOS Autolayout: проблема с UILabels при изменении размера родительского представления
У меня есть представление, содержащее только UILabel. Этот ярлык содержит многострочный текст. Родитель имеет переменную ширину, которая может быть изменена с помощью жесты панорамирования. Моя проблема в том, что когда я делаю это изменение размера, UILabel не пересчитывает его высоту, так что весь контент все еще виден, он просто отключает его.
Мне удалось исправить это с помощью взлома, но это ужасно медленно запускать:
- (void)layoutSubviews {
CGSize labelSize = [self.labelDescription sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];
if (self.constraintHeight) {
[self removeConstraint:self.constraintHeight];
}
self.constraintHeight = [NSLayoutConstraint constraintWithItem:self.labelDescription attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:labelSize.height];
[self addConstraint:self.constraintHeight];
[super layoutSubviews];
}
Не должно ли это происходить автоматически с автозапуском?
ИЗМЕНИТЬ
Структура моего представления:
UIScrollView
---> UIView
---> UILabel
Вот ограничения на UIScrollView:
<NSLayoutConstraint:0x120c4860 H:|-(>=32)-[DescriptionView:0x85c81c0] (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x120c48a0 H:|-([email protected])-[DescriptionView:0x85c81c0] priority:900 (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x120c48e0 H:[DescriptionView:0x85c81c0(<=576)]>,
<NSLayoutConstraint:0x120c4920 H:[DescriptionView:0x85c81c0]-(>=32)-| (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x120c4960 H:[DescriptionView:0x85c81c0]-([email protected])-| priority:900 (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x8301450 DescriptionView:0x85c81c0.centerX == UIScrollView:0x85db650.centerX>,
Вот ограничения на UIView:
<NSLayoutConstraint:0x85c4580 V:|-(0)-[UILabel:0x85bc7b0] (Names: '|':DescriptionView:0x85c81c0 )>,
<NSLayoutConstraint:0x85c45c0 H:|-(0)-[UILabel:0x85bc7b0] (Names: '|':DescriptionView:0x85c81c0 )>,
<NSLayoutConstraint:0x85c9f80 UILabel:0x85bc7b0.trailing == DescriptionView:0x85c81c0.trailing>,
<NSLayoutConstraint:0x85c9fc0 UILabel:0x85bc7b0.centerY == DescriptionView:0x85c81c0.centerY>
UILabel сам по себе не имеет ограничений, кроме того, что он привязывает его к краям его родительского
Ответы
Ответ 1
Я исправил эту проблему после поднятия ошибки с Apple. Проблема в том, что многострочный текст требует двухпроходного подхода к компоновке, и все это зависит от свойства preferredMaxLayoutWidth
Вот соответствующий код, который необходимо добавить в контроллер просмотра, содержащий многострочную метку:
- (void)viewWillLayoutSubviews
{
// Clear the preferred max layout width in case the text of the label is a single line taking less width than what would be taken from the constraints of the left and right edges to the label superview
[self.label setPreferredMaxLayoutWidth:0.];
}
- (void)viewDidLayoutSubviews
{
// Now that you know what the constraints gave you for the label width, use that for the preferredMaxLayoutWidth—so you get the correct height for the layout
[self.label setPreferredMaxLayoutWidth:[self.label bounds].size.width];
// And then layout again with the label correct height.
[self.view layoutSubviews];
}
Ответ 2
Хорошо, я, наконец, прибил его. Решение состоит в том, чтобы установить preferredMaxLayoutWidth
в viewDidLayoutSubviews
, но только после первого раунда макета. Вы можете организовать это просто, отправив асинхронно обратно в основной поток. Итак:
- (void)viewDidLayoutSubviews {
dispatch_async(dispatch_get_main_queue(), ^{
self.theLabel.preferredMaxLayoutWidth = self.theLabel.bounds.size.width;
});
}
Таким образом, вы не устанавливаете preferredMaxLayoutWidth
до тех пор, пока ширина метки не будет правильно установлена с помощью ограничений, связанных с супервизором.
Рабочий пример проекта здесь:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch23p673selfSizingLabel4/p565p579selfSizingLabel/ViewController.m
EDIT: Другой подход! Подкласс UILabel и переопределение layoutSubviews
:
- (void) layoutSubviews {
[super layoutSubviews];
self.preferredMaxLayoutWidth = self.bounds.size.width;
}
В результате получается метка саморазмера - она автоматически изменяет свою высоту, чтобы разместить ее содержимое независимо от того, как изменяется ее ширина (при условии, что ее ширина изменяется с помощью ограничений/макета).
Ответ 3
В Xcode 6.1 для iOS 7/8 я смог заставить это работать, просто установив preferredMaxLayoutWidth
в методе setter, который вызвал мое представление, чтобы отобразить текст для метки. Я предполагаю, что он был установлен в 0
для начала. Пример ниже, где self.teachPieceLabel
- это метка. Метка соединена с ограничениями рядом с другими метками в представлении в Interface Builder.
- (void)setTeachPieceText:(NSString *)teachPieceText {
self.teachPieceLabel.text = teachPieceText;
[self.teachPieceLabel setPreferredMaxLayoutWidth:[self.teachPieceLabel bounds].size.width];
}