Ios10: ширина/высота кадра viewDidLoad не инициализированы правильно
Начиная с обновления до XCode8 GM и ios10, все мои представления, созданные с помощью Interface Builder, не инициализируются правильно до тех пор, пока они не будут намного позже ожидаемых. Это означает, что в viewDidLoad, cellForRowAtIndexPath, viewWillAppear и т.д. Размер кадра равен {1000,1000} для каждого представления. В какой-то момент они, похоже, исправляют, но слишком поздно.
Первая возникшая проблема связана с общим округлением углов, которые не проходят по всем направлениям:
view.layer.cornerRadius = view.frame.size.width/2
Дальнейшие проблемы отображаются для всего, что зависит от размера кадра для выполнения вычислений в коде.
cellForRowAtIndexPath
Для cellForRowAtIndexPath размер кадра не работает на начальном отображении таблицы, но затем отлично работает после его прокрутки. willDisplayCell: forRowAtIndexPath также не имеет правильного размера рамки.
Я жестко закодировал несколько значений, но, очевидно, это очень плохая практика кода, а также довольно много в моих проектах.
Есть ли способ или место для получения правильных размеров кадров?
ИЗМЕНИТЬ
Я обнаружил, что использование ограничения высоты/ширины вместо высоты ширины кадра является более надежным. Это может добавить накладные расходы на необходимость много новых IBOutlets для связывания ограничений высоты и ширины элементов.
На данный момент я создал категорию UIView, которая позволяет мне напрямую обращаться к ограничениям высоты и ширины вида без IBOutlets. Для минимального использования малая петля не должна иметь большого значения. Результаты, не гарантированные для элементов IB без ограничений ширины и высоты, созданы, очевидно. Вероятно, возвращает 0 в лучшем случае для константы, или, что еще хуже. Кроме того, если у вас нет ограничения по высоте и ширине, и размер вашего представления динамически зависит от ведущих/конечных ограничений, это не сработает.
-viewDidLoad имеет правильный размер кадра, но часто приводит к визуальному изменению пользовательского интерфейса, если вы здесь делаете изменения.
UIView + WidthHeightConstraints.h
@interface UIView (WidthHeightConstraints)
-(NSLayoutConstraint*)widthConstraint;
-(NSLayoutConstraint*)heightConstraint;
-(NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute;
@end
UIView + WidthHeightConstraints.m
#import "UIView+WidthHeightConstraints.h"
@implementation UIView (WidthHeightConstraints)
-(NSLayoutConstraint*)widthConstraint{
return [self constraintForAttribute:NSLayoutAttributeWidth];
}
-(NSLayoutConstraint*)heightConstraint {
return [self constraintForAttribute:NSLayoutAttributeHeight];
}
-(NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute {
NSLayoutConstraint *targetConstraint = nil;
for (NSLayoutConstraint *constraint in self.constraints) {
if (constraint.firstAttribute == attribute) {
targetConstraint = constraint;
break;
}
}
return targetConstraint;
}
@end
РЕДАКТИРОВАТЬ 2
Категория выше доказала свою эффективность лишь частично. В основном потому, что ios автоматически добавляет несколько дополнительных дубликатов ограничений высоты и ширины, которые имеют тип NSContentSizeLayoutConstraint, которые на самом деле не имеют тот же размер, что и нормальное ограничение. NSContentSizeLayoutConstraint также является частным классом, поэтому я не могу сделать isKindOfClass, чтобы отфильтровать их. Я еще не нашел другого способа эффективно протестировать их. Это раздражает.
Ответы
Ответ 1
Наиболее распространенные проблемы, которые вы описываете, появляются только в iOS 10 и могут быть решены путем добавления этой строки (при необходимости):
self.view.layoutIfNeeded()
чуть выше кода, который отвечает за изменение ограничений, layer.cornerRadius и т.д.
ИЛИ
поместите свой код, связанный с кадрами/слоями, в метод viewDidLayoutSubviews()
:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.layer.cornerRadius = self.myView.frame.size.width/2
view.clipsToBounds = true
... etc
}
Ответ 2
Мы создали радар (28342777 (обозначенный как дубликат для 28221021, но Open)) для подобной проблемы, и ответ, который мы получили, был следующим:
"Благодарим вас за сообщение об этой проблеме. Не могли бы мы получить дополнительную информацию о представлении изображения профиля? В Xcode 8 полное ограничение, не исправляемое изображение больше не сохраняет фрейм для минимизации различий и поддержки автоматического обновления фреймов в IB Во время выполнения эти представления декодируются с размером заполнитель 1000x1000, но разрешаются после первого макета. Может ли изображение быть назначено перед первоначальной компоновкой и присвоить изображение представлению изображения после первого адреса макета в этом случае? образец, чтобы помочь нам в дальнейшем анализировать. спасибо!"
В настоящее время мы предоставили им образец проекта. Мои наблюдения:
- Проблема, которую мы использовали для XIB, которые преобразуются из Xcode 7.x в Xcode 8.x
- Если мы намеренно сломаем ограничение в XIB, тогда viewDidLoad получит ожидаемую высоту и ширину, а не 1000x1000.
- Для нас это был UIImageView, на котором мы применяли некоторые слои, чтобы сделать его круговым и использовать masksToBounds. Если мы установим masksToBounds = NO, тогда мы все работаем нормально.
Хотя Apple утверждает, что он будет стандартом от Xcode 8, который будет установлен в 1000x1000, поведение не кажется последовательным.
Надеюсь, это поможет.
Ответ 3
Я столкнулся с той же проблемой и попытаюсь решить ее без везения, указав выше предложения.
Кажется, это ошибка для Apple. Я, наконец, нашел решение, изменив, чтобы сохранить мой документ XIB обратно в формат Xcode 7.x, и мой интерфейс снова стал нормальным.
Пока Apple не выпустит исправление, я не хочу тратить свое время на ее взлом.
![введите описание изображения здесь]()
Ответ 4
Как это сделать:
- (NSLayoutConstraint*)widthConstraint{
return [self constraintForAttribute:NSLayoutAttributeWidth];
}
- (NSLayoutConstraint*)heightConstraint {
return [self constraintForAttribute:NSLayoutAttributeHeight];
}
- (NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute {
NSLayoutConstraint *targetConstraint = nil;
for (NSLayoutConstraint *constraint in self.constraints) {
//NSLog(@"constraint: %@", constraint);
if (![constraint isKindOfClass:NSClassFromString(@"NSContentSizeLayoutConstraint")]) {
if (constraint.firstAttribute == attribute) {
targetConstraint = constraint;
break;
}
}
}
return targetConstraint;
}
Ответ 5
Вы никогда не должны полагаться на время, когда выкладывается представление. Если это сработало для вас раньше, то из чистой удачи. В UIKit очень мало гарантий об этом. Если вы полагаетесь на что-то, принимающее размер вашего взгляда, правильная вещь - переопределить layoutSubviews
в этом представлении и настроить там свои вещи.
Даже после того, как ваше представление полностью отображается на экране, все еще существует так много условий, которые могут привести к изменению размера представления. Например: двойная строка состояния, многозадачность на iPad, вращение устройства, просто чтобы назвать несколько. Поэтому никогда не рекомендуется делать изменения, связанные с фреймом, в определенный момент времени.
Ответ 6
У меня была такая же проблема. У меня были пользовательские подклассы UITableViewCell и я использовал clipsToBounds = YES
и self.iconView.layer.cornerRadius = self.iconView.frame.size.width/2
, чтобы дать себе круговое изображение. Пробовал вызывать метод настройки моей ячейки из cellForRowAtIndexPath
и willDisplayCell
и ни один из них не работал.
Вот что работает:
Переместите свой код слоя в метод ячейки -layoutSubviews
следующим образом:
-(void)layoutSubviews {
[super layoutSubviews];
self.iconView.clipsToBounds = YES;
self.iconView.layer.cornerRadius = self.iconView.frame.size.width/2;
}
После этого изображения должны загружаться должным образом, и ваш код выравнивания также должен работать.
Ответ 7
Только в Обновить фрейм в поле автозагрузки ![введите описание изображения здесь]()