Подтвердить что ты не робот

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, чтобы отфильтровать их. Я еще не нашел другого способа эффективно протестировать их. Это раздражает.

4b9b3361

Ответ 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

Только в Обновить фрейм в поле автозагрузки введите описание изображения здесь