UIInterfaceOrientation еще не обновлен, когда UIDeviceOrientationDidChangeNotification пойман в UIView

Я работаю над библиотекой с открытым исходным кодом, чтобы обеспечить представление ввода текста в стиле iMessage — MessageComposerView (относится к вопросу). Представление будет прилипать к клавиатуре, как inputAccessoryView, и увеличиваться вместе с текстом, но не исчезнет, ​​когда это сделает клавиатура.

Недавно я столкнулся с невыносимой проблемой при повороте этого пользовательского UIView, который появляется только в том случае, если представление было создано через initWithFrame. В основном в тот момент, когда было перехвачено уведомление UIDeviceOrientationDidChangeNotification, если представление было создано через initWithFrame, UIInterfaceOrientation и кадр еще НЕ были обновлены. Вот оба способа создания экземпляров.

loadNibNamed:

self.messageComposerView = [[NSBundle mainBundle] loadNibNamed:@"MessageComposerView" owner:nil options:nil][0];
self.messageComposerView.frame = CGRectMake(0,
                                            self.view.frame.size.height - self.messageComposerView.frame.size.height,
                                            self.messageComposerView.frame.size.width,
                                            self.messageComposerView.frame.size.height);

initWithFrame:

self.messageComposerView = [[MessageComposerView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height-54, 320, 54)];

При запуске через loadNibNamed и повороте в альбомную, при получении уведомления UIDeviceOrientationDidChangeNotification:

  • UIDeviceOrientation = [[объект уведомления] ориентация] = UIInterfaceOrientationLandscapeLeft
  • UIInterfaceOrientation = [Общее приложение UIApplication].statusBarOrientation = UIDeviceOrientationLandscapeRight
  • self.frame.size = (ширина=480, высота=90)

Здесь важно отметить, что и интерфейс, и устройство имеют альбомную ориентацию, а ширина уже была изменена на альбомную (тестирование на симуляторе iPhone 6.1). Теперь выполняем тот же тест, но с использованием initWithFrame:

  • UIDeviceOrientation = [[объект уведомления] ориентация] = UIDeviceOrientationLandscapeRight
  • UIInterfaceOrientation = [Общее приложение UIApplication].statusBarOrientation = UIInterfaceOrientationPortrait
  • self.frame.size = (ширина=320, высота=54)

На этот раз обратите внимание, что ориентация интерфейса по-прежнему книжная и ширина кадра НЕ изменилась на альбомную. Если я установлю точку останова в методе setFrame:(CGRect)frame, я увижу, что фрейм установлен в ландшафтный фрейм ПОСЛЕ того, как UIDeviceOrientationDidChangeNotification был перехвачен и обработан.

Оба метода инициализации имеют почти идентичные настройки:

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.frame = frame;
        self.sendButton = [[UIButton alloc] initWithFrame:CGRectZero];
        self.messageTextView = [[UITextView alloc] initWithFrame:CGRectZero];
        [self setup];
        [self addSubview:self.sendButton];
        [self addSubview:self.messageTextView];

    }
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    [self setup];
}

Когда setup выполняет необходимые настройки вида:

- (void)setup {
    self.backgroundColor = [UIColor lightGrayColor];
    self.autoresizesSubviews = YES;
    self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth;
    self.userInteractionEnabled = YES;
    self.multipleTouchEnabled = NO;

    CGRect sendButtonFrame = self.bounds;
    sendButtonFrame.size.width = 50;
    sendButtonFrame.size.height = 34;
    sendButtonFrame.origin.x = self.frame.size.width - kComposerBackgroundRightPadding - sendButtonFrame.size.width;
    sendButtonFrame.origin.y = kComposerBackgroundRightPadding;
    self.sendButton.frame = sendButtonFrame;
    self.sendButton.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin;
    self.sendButton.layer.cornerRadius = 5;
    [self.sendButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [self.sendButton setTitleColor:[UIColor colorWithRed:210/255.0 green:210/255.0 blue:210/255.0 alpha:1.0] forState:UIControlStateHighlighted];
    [self.sendButton setTitleColor:[UIColor grayColor] forState:UIControlStateSelected];
    [self.sendButton setBackgroundColor:[UIColor orangeColor]];
    [self.sendButton setTitle:@"Send" forState:UIControlStateNormal];
    self.sendButton.titleLabel.font = [UIFont boldSystemFontOfSize:14];


    CGRect messageTextViewFrame = self.bounds;
    messageTextViewFrame.origin.x = kComposerBackgroundLeftPadding;
    messageTextViewFrame.origin.y = kComposerBackgroundTopPadding;
    messageTextViewFrame.size.width = self.frame.size.width - kComposerBackgroundLeftPadding - kComposerTextViewButtonBetweenPadding - sendButtonFrame.size.width - kComposerBackgroundRightPadding;
    messageTextViewFrame.size.height = 34;
    self.messageTextView.frame = messageTextViewFrame;
    self.messageTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
    self.messageTextView.showsHorizontalScrollIndicator = NO;
    self.messageTextView.layer.cornerRadius = 5;
    self.messageTextView.font = [UIFont systemFontOfSize:14];
    self.messageTextView.delegate = self;

    [self addNotifications];
    [self resizeTextViewForText:@"" animated:NO];
}

Итак, мой вопрос: почему моя пользовательская инициализация UIView через initWithFrame по-прежнему имеет портретную рамку и ориентацию интерфейса, а также время получения UIDeviceOrientationDidChangeNotification. Я использую это уведомление, чтобы выполнить настройку кадра, и мне нужно, чтобы ширина уже была обновлена ​​​​до ширины ландшафта.

Я надеюсь, что есть какое-то свойство авторотации, которого мне не хватает в программной настройке, которая похоронена где-то в XIB, но просто не могу ее найти. Я просмотрел, наверное, дюжину UIDeviceOrientationDidChangeNotification сообщений stackoverflow, но не нашел решения.


person alexgophermix    schedule 14.11.2013    source источник
comment
Вы можете удалить строку self.frame = frame; в коде инициализации. Вызов super уже должен это делать.   -  person Michael Enriquez    schedule 17.11.2013


Ответы (1)


MessageComposerView выглядит аккуратно, спасибо, что поделились.

Вместо того, чтобы слушать UIDeviceOrientationDidChangeNotification, вы должны реализовать layoutSubviews. Он вызывается в ответ на вращение и все остальное, что системе потребуется UIView для изменения положения своих подпредставлений (https://stackoverflow.com/a/5330162/1181421). Там вы можете расположить свои подпредставления относительно фрейма UIView, и вы можете быть уверены, что фрейм будет правильным.

person Michael Enriquez    schedule 17.11.2013
comment
Я не мог бы быть более благодарен вам. Я был настолько зациклен на том, чтобы решить, почему размер кадра еще не был изменен, когда был пойман UIDeviceOrientationDidChangeNotification, что я полностью проигнорировал возможность выполнения моей реконфигурации в другом месте. Мне все еще любопытно, почему возникает эта проблема, но, судя по тому факту, что я ожидал изменения UIInterfaceOrientation точно в момент изменения UIDeviceOrientation, я думаю, что мой подход изначально был ошибочным. - person alexgophermix; 17.11.2013