Самый быстрый способ рендеринга кнопок UIB с использованием слоев и Grand Central Dispatch? - PullRequest
4 голосов
/ 28 сентября 2011

У меня есть сетка из 30 UIB-кнопок и, возможно, даже больше, подкласс для рендеринга с использованием слоев: основание CALayer, CAShapeLayer, CAGradientLayer и CATextLayer. Я пытаюсь минимизировать общее время, необходимое для рендеринга / отображения кнопок при загрузке соответствующего файла XIB. Если я просто настрою каждую кнопку по очереди на viewDidLoad, то время, необходимое для появления представления, составляет около 5-6 секунд, что явно слишком много.

Для ускорения настройки кнопок я использую Grand Central Dispatch следующим образом. В viewDidLoad я настраиваю каждый слой кнопок, используя dispatch_async в глобальной очереди (добавляя к базовому слою фигуру и слои градиента), чтобы кнопки могли отображаться в другом потоке. В конце блока CATextLayer добавляется в слой градиента.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

    CGRect base_bounds = CGRectMake(0, 0, self.layer.bounds.size.width, self.layer.bounds.size.height - self.layer.bounds.size.height * 0.10f);
    CGPoint move_point = CGPointMake(0.0f, base_bounds.size.height * 0.10f);
    self.layer.masksToBounds = NO;
    baseLayer = [CALayer layer];

    baseLayer.cornerRadius = 10.0;
    baseLayer.shadowOffset = CGSizeMake(0.0f, 2.0f);
    baseLayer.shadowOpacity = 1.5f;
    baseLayer.shadowColor = [UIColor blackColor].CGColor;
    baseLayer.shadowRadius = 2.5f;
    baseLayer.anchorPoint = CGPointMake(0.5f, 0.5f);
    baseLayer.position    = move_point;

    CAShapeLayer *shape = [CALayer layer];
    shape.bounds = base_bounds;
    shape.cornerRadius = 10.0;
    shape.anchorPoint      = CGPointMake(0.0f, 0.0f);
    shape.position         = move_point;
    shape.backgroundColor = [UIColor darkGrayColor].CGColor;

    gradient = [CAGradientLayer layer];
    gradient.anchorPoint      = CGPointMake(0.0f, 0.0f);
    gradient.position         = CGPointMake(0.0f, 0.0f);
    gradient.bounds           = base_bounds;
    gradient.cornerRadius     = 10.0;
    gradient.borderColor      = [UIColor colorWithRed:0.72f
                                                green:0.72f
                                                 blue:0.72f
                                                alpha:1.0].CGColor;
    gradient.borderWidth      = 0.73;
    gradient.colors = [NSArray arrayWithObjects:
                       (id)[UIColor whiteColor].CGColor,
                       (id)[UIColor whiteColor].CGColor,
                       nil];


    [baseLayer addSublayer:shape];
    [baseLayer addSublayer:gradient];
    [self.layer addSublayer:baseLayer];

    [textLayer setBounds:gradient.bounds];
           [textLayer setPosition:CGPointMake(CGRectGetMidX(textLayer.bounds), CGRectGetMaxY(textLayer.bounds) - 6)];
           [textLayer setString:self.titleLabel.text]; 
           [textLayer setForegroundColor:[UIColor blackColor].CGColor];
           [gradient addSublayer:textLayer];

});

При таком подходе общее время сокращается примерно до 2-3 секунд. Мне интересно, если кто-нибудь может предложить более быстрый способ рендеринга кнопок. Обратите внимание, что меня не интересует какое-либо решение, которое отказывается от использования слоев.

Заранее спасибо.

Ответы [ 3 ]

3 голосов
/ 03 октября 2011

Возможно, я упускаю суть, но разве вам не лучше переопределить метод drawRect: UIButton: рисование в CoreGraphics (CG) будет рисоваться намного быстрее секунд, вы можете легко создавать градиенты, текст , изображения с CG API. Если я правильно понимаю, у вас есть 4 слоя на кнопку и более 30 кнопок в одном виде (более 120 слоев)? Если это так, я не думаю, что вы должны рисовать так много слоев (рендеринг / смешивание их всех по отдельности объясняет огромное время рендеринга). Другой возможностью было бы иметь 4 больших слоя для всех кнопок.

1 голос
/ 01 октября 2011

Вот моя идея,

Отделите ваши CALayers от UIButton - UIKit ничего не разрешит в фоновом потоке.

Если у вас есть возможность на экране, предшествующем сетке кнопок, используйте [buttonGridViewController executeSelectorInBackground: renderCALayers] для визуализации ваших CALayers в фоновом режиме.

Когда вы загружаете свою сетку кнопок в viewDidLoad, накладываете UIB-кнопки с типом UIButtonTypeCustom и backgroundColor = [UIColor clearColor] поверх ваших CALayers 'button' (убедитесь, что на UIB-кнопках вызывается takeSubviewToFront, чтобы они получали события касания) ,

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

Если у вас есть какие-либо вопросы, пожалуйста, прокомментируйте.

EDIT:

Несколько связанных предметов,

Чем заменить кнопку UIB для улучшения частоты кадров?

Дизайн пользовательского интерфейса - лучший способ справиться со многими кнопками UIB

Как получить сенсорное событие на CALayer?

Я полагаю, что единственный способ получить более быструю загрузку с этого момента - это либо заменить, либо удалить кнопки UIB в целом и перехватить события касания другим способом. Таким образом, весь рендеринг может быть выполнен в фоновом потоке перед представлением.

0 голосов
/ 04 октября 2011

Рассматривали ли вы использовать NSOperation для создания слоев вашей кнопки? В вашем коде вы создаете экземпляр очереди операций, а затем выполняете итерацию по каждой кнопке, запуская операцию (передавая соответствующие параметры, необходимые для создания слоя этой кнопки), в очередь при запуске каждой кнопки.

В рамках вашей NSOperation создайте свой слой с помощью CoreGraphics, а затем передайте слой обратно кнопке, которая инициировала операцию (в данном случае, кнопке назначения) через ваш обработчик завершения (обязательно вызовите его в главном потоке). Это должно дать вам довольно хорошую производительность, так как слой ваших кнопок будет визуализироваться из основного потока с использованием CoreGraphics, а затем возвращаться обратно в UIKit для представления только после того, как слой будет визуализирован.

NSOperation находится поверх GCD, имеет незначительные накладные расходы, но дает преимущества в вашем случае, такие как возможность устанавливать зависимые предпосылки и расстановку приоритетов.

Кроме того, я почти уверен, что вы не должны вызывать UIColor в блоке GCD, которым вы поделились.

Надеюсь, это полезно!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...