Просмотр 1000×1000 в initWithCoder:

#ios #iphone #ipad #uiview #initwithcoder

#iOS #iPhone #iPad #пользовательский просмотр #initwithcoder

Вопрос:

Есть пара вопросов, задающих этот вопрос на SO, но ни один из них не задал этого должным образом.

Я использую пользовательский индикатор выполнения, который является UIView классом на основе этой реализации.

 @implementation MCProgressBarView {
    UIImageView * _backgroundImageView;
    UIImageView * _foregroundImageView;
    CGFloat minimumForegroundWidth;
    CGFloat availableWidth;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
  self = [super initWithCoder:aDecoder];
  // [self bounds] is 1000x1000 here... what? 
  if (self) [self initialize];
  return self;
}


- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) [self initialize];
    return self;
}

- (void) initialize {
  UIImage * backgroundImage = [[[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"progress-bg" ofType:@"png"]]
                               resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 10.0f)];

  UIImage * foregroundImage = [[[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"progress-bg" ofType:@"png"]]
                               resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 10.0f)];

  _backgroundImageView = [[UIImageView alloc] initWithFrame:self.bounds];
  _backgroundImageView.image = backgroundImage;
  [self addSubview:_backgroundImageView];

  _foregroundImageView = [[UIImageView alloc] initWithFrame:self.bounds];
  _foregroundImageView.image = foregroundImage;
  [self addSubview:_foregroundImageView];

  UIEdgeInsets insets = foregroundImage.capInsets;
  minimumForegroundWidth = insets.left   insets.right;

  availableWidth = self.bounds.size.width - minimumForegroundWidth;

  [self adjustProgress:0.0f];
}
  

Я создал UIView в Interface Builder файл размером 200×20 точек и присвоил этому представлению этот пользовательский класс, который MCProgressBarView я использую. Когда приложение запускается, initWithCoder запускается и создает представление прогресса размером 1000×1000 точек, не обращая внимания на размер, который имеет представление в IB.

Как мне заставить initWithCoder запускаться с правильным размером, назначенным в IB?

ПРИМЕЧАНИЕ: Я не хочу жестко привязывать это к 200×20 и не хочу устанавливать это с помощью кода во время выполнения. Есть ли способ заставить это работать?

Ответ №1:

TL; DR: Переместите свою логику фрейма / границ в viewDidLayoutSubviews .

initWithCoder: небезопасно для выполнения логики фрейма. Для этого вам следует использовать viewDidLayoutSubviews . Apple использует границы 1000×1000, начиная с iOS 10. Неясно, ошибка ли это или преднамеренный, но, похоже, у этого есть чистый положительный результат — люди приходят сюда и спрашивают об этом. ;-)

На самом деле, initWithCoder: такая логика никогда не была безопасной. Раньше у вас был бы вид, границы которого совпадали бы с границами экрана iPhone 5, потому что это то, что вы использовали в IB, но затем вид увеличился бы до размера iPhone 6 Plus или iPad, и это вызвало бы визуальные проблемы.

Теперь Apple просто устанавливает границы в 1000×1000, что неверно для всех случаев. Границы исправляются после выполнения прохода макета в представлении.

Комментарии:

1. В классе на основе UIView нет viewDidLayoutSubviews. Если я использую ViewController для правильной настройки размера представления, то все это плохо задумано Apple как множество вещей на ios.

2. Использование @SpaceDog layoutSubviews .

3. ОК. Я реализовал layoutSubviews в классе. Когда выполняется этот метод, вид имеет правильный размер без необходимости что-либо делать, но когда он отображается в приложении, он равен 1000×1000.

4. Пожалуйста, опубликуйте скриншоты. Что-то звучит странно.