2015-11-26 4 views
5

Во-первых, я должен упомянуть, что это в основном проблема эффективности.Расчет рамок в `viewDidLayoutSubviews`

Существует много дискуссий относительно того, где делать кадровые вычисления, где viewWillAppear слишком рано, и viewDidAppear слишком поздно (вид уже виден).

Общий ответ заключается в том, чтобы сделать расчеты кадра в viewDidLayoutSubviews. Проблема в том, что она вызывается несколько раз. Хуже того, самый точный вызов, тот, где все кадры имеют окончательный размер, является последним. Насколько я знаю, нет способа узнать, какой звонок является последним.

Мы используем флаг «frameAreSet» (инициализированный ложь) и проверку, если фрейм не равен нулю (что-то вроде self.view.frame.size.width != 0), а «framesAreSet» является ложным, он входит, поворачивает флаг и вычисляет только один раз , Что-то вроде:

- (void)viewDidLayoutSubviews 
{ 
    [super viewDidLayoutSubviews]; 

    if (self.view.frame.size.width != 0 && !framesAreSet) 
    { 
     framesAreSet = true; 

     //Calculate frames here 
    } 
} 

Это выглядит нормально, но на самом деле, проверка на что-то вроде self.view.frame.size.width != 0 делает не гарантии того, что кадры действительно множество. Тот факт, что viewDidLayoutSubviews будет вызван, предполагает, что некоторые фреймы не установлены в их конечное состояние.

Было бы здорово иметь viewCompleteLayoutSubviews. Любые идеи о том, какой лучший способ выполнить вычисления «одноразового» кадра, когда все кадры установлены, и представление еще не видно?

(Это, вероятно, проблема с видом не с использованием NSConstraints)

+1

Почему это проблема, вызываемая 'viewDidLayoutSubviews' более одного раза? С какими проблемами вы сталкиваетесь? – rmaddy

+0

Вы случайно не обновляете границы 'self.view' там? Если это ваша проблема. – Clafou

+0

@rmaddy Резервирование - это не хорошая практика кодирования. Предположим, у вас есть сложный ViewController, добавив много подпрограмм в коде (фактически созданных в основном в коде) и выполняйте множество вычислений для фреймов настроек. На самом деле, представьте, что «тяжелые» вычисления вызывают столько раз, сколько добавляют subviews plus. Имейте в виду, что все, кроме последнего, являются излишними. Иногда «это работает» недостаточно. – bauerMusic

ответ

1

Приложение получает только один didLayoutSubviews за ничью (до этого момента изменения состояния видны только с setNeedsLayout). В этом смысле didLayoutSubviewsis Ваша идея didCompleteLayoutOfSubviews. После розыгрыша, если произойдет другое изменение состояния представления, макет снова не завершен.

Другими словами, количество звонков didLayout не зависит от количества добавлений подкадров или изменений фрейма, это зависит от количества ничьих (не путать с циклом запуска). Перед рисованием, если установлен флажок needLayout, layoutSubviews, а затем didLayoutSubviews будут вызываться ровно один раз, независимо от того, насколько иерархия представлений была перегруппирована.

+0

Если я правильно понимаю, количество вызовов для 'didLayoutSubviews' зависит (помимо начального) от моего изменения (изменить кадр, добавить subview) в код. То есть, это предсказуемо, и у меня может быть флаг после моего последнего триггера didLayoutSubviews, после которого 'didLayoutSubviews' будет вызываться в течение последнего времени (для этого цикла)? – bauerMusic

+1

Я не думаю, что ваше понимание совершенно верно. Количество вызовов didLayout не зависит от количества добавлений subview или изменений фрейма, это зависит от количества ничьих (не путать с циклом запуска). Перед рисованием, если установлен флажок needLayout, layoutSubviews, а затем didLayoutSubviews будут вызываться ровно один раз, независимо от того, насколько иерархия представлений была перегруппирована. – danh

+0

Хороший ответ. Было бы здорово, если бы вы могли добавить свой последний комментарий к своему ответу, я отмечу его как «принятый». Мне нужно найти, где мои представления вызывают рисовать после начального «layoutSubviews». Но это совсем другой вопрос. Благодаря! – bauerMusic

0

Вы можете рассчитать кадр в методе viewDidLayoutSubviews с помощью отправки сразу токенов (НОД), что если кадры инициализируется один раз это не проникнет внутрь вашего диспетчерский блок.

+0

Отличная точка. Однако, для моей цели, не будет отличаться от флага. Кроме того, существует проблема «ловить» последний и самый обновленный вызов viewDidLayoutSubviews. – bauerMusic

+0

Но я не думаю, что мы можем получить последний вызов в viewDisLayoutSubview .... вы можете делать вычисления кадров в viewWillAppear. – Abhishek

+0

Как я уже писал, в разных условиях рамки 'viewWillAppear' не имеют окончательного размера. – bauerMusic

1

Вы должны всегда переустанавливать размер, который ваш вид имеет в настоящее время.

Вы устанавливаете рамки, а затем поворачиваете устройство. Вам нужно пересчитать фреймы? Конечно, вы изменили размер границ! Вы делаете опасные предположения с framesAreSet.

Если производительность действительно для вас важна, вы можете захотеть кэшировать вычисленные кадры каким-либо словарем, чтобы они недействительны, если границы изменены.

Но вы, разумеется, не сможете избежать выполнения макета только один раз.

Альтернативой было бы сделать команду контроллера вида view экземпляром пользовательского класса и переопределить -layoutSubviews.

+0

_ «Вы делаете опасные предположения с вашими рамкамиAreSet». В какой-то момент он устанавливается, например, 'viewDidAppear'. Рассмотрим представление, которое не вращается, имеет ли смысл несколько раз выполнять один и тот же расчет кадров? Хуже того, если представление вращается, должно ли каждое вращение выполнять вычисления кадров несколько раз, где единственное полезное время последнего? – bauerMusic

+0

«Рассмотрите представление, которое не вращается, имеет ли смысл повторять один и тот же расчет кадров несколько раз?» - Да, есть другие вещи, которые могут привести к тому, что ваш взгляд изменит свой размер. –

Смежные вопросы