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

Как ускорить работу UITableView?

У меня есть UITableView с примерно 400 ячейками в 200 разделах и немного вяло реагирует на взаимодействие с пользователем (прокрутка, выбор ячеек). Я убедился, что методы для извлечения ячеек и заголовков минимально минимальны. и я не думаю, что я делаю что-то необычное, чтобы сделать его медленным. Ячейки и заголовки имеют только фоновое изображение и текст. У кого-нибудь еще была такая проблема, и знаете ли вы каким-либо способом заставить ее работать немного быстрее?

Изменить: Я предлагаю щедрость, потому что я хотел бы получить полезную информацию об этом. Я не думаю, что ответ кроется в проблеме в моем коде. Вместо этого я ищу стратегии для реорганизации UITableView, чтобы он работал быстрее. Я полностью открыт для добавления нового кода, и я с нетерпением жду того, что вы, ребята, должны сказать.

Вялость наблюдается как на симуляторе, так и на моем устройстве, iPhone 4. Вот мои реализации viewForHeaderInSection и cellForRowAtIndexPath, которые являются единственными UITableViewDelegate методами, реализованными нетривиально. Я повторно использую ячейки и заголовки.

- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger) section
{
    HaikuHeaderView* view= [m_sectionViews objectAtIndex:section];
    NSMutableArray* array= [m_haikuSearch objectAtIndex:section];
    Haiku* haiku= [array objectAtIndex:0];

    [view.poetLabel setText:[haiku nameForDisplay]];

    return view;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];

        cell.backgroundView= [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cell gradient2.png"]];

        // (Set up a bunch of label attributes in the cell...)
    }

    NSMutableArray* array= [m_haikuSearch objectAtIndex:indexPath.section];
    Haiku* haiku = [array objectAtIndex:indexPath.row];
    cell.textLabel.text = [haiku.m_lines objectAtIndex:0];

    return cell;
}
4b9b3361

Ответ 1

Даже если ваша ячейка на самом деле такая простая (фоновое изображение и ярлык), нужно рассмотреть некоторые вещи

Кэширование изображений Это очевидная вещь - если вы используете одно и то же изображение во всем мире, загрузите его один раз в UIImage и повторно используйте его. Даже если система будет кэшировать его самостоятельно, прямо с использованием уже загруженного никогда не должно быть больно.

Быстрый расчет Другая довольно очевидная вещь - сделать расчет высоты и содержимого как можно быстрее. Не выполняйте синхронные выборки (сетевые вызовы, чтения дисков и т.д.).

Альфа-канал в образе Что дорого, когда чертеж является прозрачностью. Поскольку у вашего фона ячейки ничего нет, убедитесь, что вы сохранили изображение без альфа-канала. Это экономит много обработки.

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

Пользовательская ячейка В общем, подклассификация UITableViewCell и реализация drawRect: сами быстрее, чем построение иерархии подзаголовков. Вы можете сделать свое изображение переменной класса, используемой всеми экземплярами. В drawRect: вы нарисуете изображение и текст поверх него.

Проверить компоновку В симуляторе есть инструмент для выделения деталей, которые рентабельны из-за прозрачности (зеленый цвет в порядке, красный - альфа-смешивание). Его можно найти в меню отладки: "Цветные смешанные слои"

Ответ 2

Лучшее, что вы можете сделать, если хотите ускорить свой код, - это профилировать его. Для этого есть две причины:

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

  • Невозможно узнать, действительно ли изменения, которые вы делаете, чтобы ускорить работу, действительно имеют значение, если у вас нет некоторых цифр для измерения. Если вы можете показать, что ваш код тратил 80% своего времени в одной рутине, и вы сократили это до 35%, вы знаете, что добились прогресса.

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

Ответ 3

Просто отметьте эти моменты.

  • Вы повторно используете ячейки. хорошая практика делать.
  • Убедитесь, что вы ничего не делаете дорогостоящие расчеты в обратный вызов cellForRowAtIndexPath или в функции, вызванной CellForRowAtIndexPath..
  • Вы сказали, что есть фон образ. Еще одна причина, по которой вы должны повторно используйте свою ячейку.

Некоторая хорошая информация о повторном использовании ячеек здесь..

РЕДАКТИРОВАТЬ: эта страница была очень поздней.

Эта тема SO question может помочь вам... особенно принятый ответ...

Ответ 4

  • используйте экземпляр общего изображения для фона (вы выделяете/инициализируете/выпускаете один раз каждый раз, когда создается новая ячейка). Когда ваш вид таблицы большой, это означает, что ячейки фона X в памяти занимают гораздо больше памяти, чем нужно.

    вместо cell.backgroundView= [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cell gradient2.png"]];
    просто используйте:
    cell.backgroundView= [SomeHelperClass sharedBackgroundUIImageResource];

  • Если это не помогает, используйте CG вместо меток и других подзаголовков (скриншот поможет здесь... узнать, о чем мы говорим).

Ответ 5

Представлен ли делегат представления таблицы:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

Если это так, вы можете захотеть вместо этого установить свойство UITableViewCell rowHeight.

Ответ 6

Используете ли вы много подзаголовков?

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

Для этого вам нужно подклассифицировать UITableViewCell и реализовать метод -(void)drawRect:(CGRect)rect.

Ответ 7

Два предложения: один - использовать -initWithStyle:reuseIdentifier: для ваших табличных ячеек таблицы вместо -initWithFrame:. Другой - прокомментировать настройку cell.backgroundView на изображение с градиентом и посмотреть, является ли это виновником. Каждый раз, когда у меня была низкая производительность в представлении таблицы, это было из-за изображения.

Ответ 8

Здесь немного не по теме (потому что у вас есть только одно фоновое изображение для всех ячеек):

Мое приложение отображает разные изображения в каждой ячейке. После добавления около 5 ячеек в UITableView - таблица резко замедляется. Чтобы обрабатывать все изображения каждый раз, когда я открываю контроллер просмотра, требуется около 1-2 секунд.

if let image = UIImage(contentsOfFile: photoFile){
    // just set it and let system fit it
    //cell.imageView!.image = image

    // 1 - Calculate sized
    let DEFAULT_THUMBNAIL_WIDTH: CGFloat  = (cellHeight / 4) * 5;
    let DEFAULT_THUMBNAIL_HEIGHT: CGFloat = cellHeight;

    let aspectRatio: CGFloat = image.size.width / image.size.height
    var willBeHeight = DEFAULT_THUMBNAIL_HEIGHT
    var willBeWidth  = DEFAULT_THUMBNAIL_HEIGHT * aspectRatio

    if(willBeWidth > DEFAULT_THUMBNAIL_WIDTH){
        willBeWidth = DEFAULT_THUMBNAIL_WIDTH
        willBeHeight = willBeWidth / aspectRatio
    }

    let eps:CGFloat = 0.000001
    assert((willBeHeight - eps) <= DEFAULT_THUMBNAIL_HEIGHT);
    assert((willBeWidth - eps) <= DEFAULT_THUMBNAIL_WIDTH);

    // 2 - Create context
    var size:CGSize = CGSize(
        width: DEFAULT_THUMBNAIL_WIDTH,
        height: DEFAULT_THUMBNAIL_HEIGHT)
    UIGraphicsBeginImageContext(size)

    // one-to-one rect
    //var imageRect: CGRect = CGRectMake(0.0, 0.0, size.width, size.height)
    var imageRect: CGRect = CGRectMake(0.0, 0.0, willBeWidth, willBeHeight)

    // 3 - Draw image
    image.drawInRect(imageRect)
    var imageResult: UIImage = UIGraphicsGetImageFromCurrentImageContext()
    cell.imageView!.image = imageResult

    UIGraphicsEndImageContext()
}else{
    DDLogError("Can not draw photo: \(photoFile)")
}

Итак, я закончил создание небольших THUMBNAILS для всех моих изображений.

Ответ 9

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