Как я могу ускорить 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;
}

person Luke    schedule 30.05.2011    source источник
comment
Было бы полезно, если бы вы могли показать нам, как вы реализовали эти методы. Не весь код, а только базовая логика   -  person Robin    schedule 30.05.2011
comment
На каких устройствах вы тестируете? Или медлительность наблюдается и на вашем Mac?   -  person BoltClock    schedule 30.05.2011
comment
См. ответ на этот вопрос, я подробно рассказываю об оптимизации вашего кода для просмотра таблиц. HTH   -  person AliSoftware    schedule 21.06.2011
comment
Возможный дубликат, отвечающий на ваш вопрос: Как улучшить производительность прокрутки iPhone UITableView?   -  person PengOne    schedule 22.06.2011
comment
как насчет того, чтобы прочитать изображение один раз из файла n, используя этот объект вместо чтения каждый раз из файла .... [UIImage imageNamed: @cell gradient2.png]];   -  person saurabh    schedule 22.06.2011


Ответы (9)


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

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

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

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

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

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

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

person Eiko    schedule 23.06.2011
comment
Хороший момент, убедитесь, что все представления (table view, imageView и т. Д.) Непрозрачны и имеют сплошной цвет (без альфа-канала, без clearColor) ... вы не всегда можете получить 100% от этого и по-прежнему иметь красивый интерфейс, но вы можете исключить 85% дорогостоящих мер по обеспечению прозрачности. Это мне очень помогло. Также есть способ запустить инструменты с тестом CoreGraphics для поиска представлений с альфа-областями ... - person Greg Combs; 23.06.2011
comment
Еще одно замечание относительно настраиваемого рисования ... Рисование изображений часто происходит быстрее при простом добавлении UIImageView - рекомендуется попробовать и профилировать. - person Eiko; 26.06.2011
comment
Спасибо за вашу помощь - виноватым было рисование тени на тексте с помощью QuartzCore. - person Luke; 11.07.2011
comment
В инструментах есть инструмент для выделения ... Не забывайте, какой инструмент? - person Kevin; 27.11.2015
comment
Собственно мой вопрос. Все говорят мне использовать инструменты - и я так и поступаю. Но где смотреть? - person Martin Algesten; 30.11.2015
comment
@MartinAlgesten Теперь его можно найти прямо в симуляторе (меню отладки). Я добавил это к своему ответу. - person Eiko; 03.12.2015
comment
@Kevin Теперь его можно найти прямо в симуляторе (меню отладки). Я добавил это к своему ответу. - person Eiko; 03.12.2015
comment
+1 для быстрого расчета; в моем случае расчет высоты строки. Загрузка 800+ ячеек с неоптимизированным вычислением высоты строки на основе текстового содержимого была чрезвычайно медленной. Быстрый тест: использование фиксированной высоты строки сделало загрузку представления таблицы почти мгновенной. Определенно то, на что стоит обратить внимание. - person Gavin Hope; 17.02.2016

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

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

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

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

person Caleb    schedule 21.06.2011
comment
Но какой инструмент вы бы использовали? - person Martin Algesten; 30.11.2015
comment
Когда вы хотите ускорить код, вам обычно нужен Time Profiler, если вы еще не знаете, что ищете что-то более конкретное, например, узкое место в графике. - person Caleb; 30.11.2015
comment
Спасибо! Я попробую это. - person Martin Algesten; 30.11.2015

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

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

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

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

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

person Krishnabhadra    schedule 30.05.2011
comment
Спасибо за информацию, я понимаю, что я был довольно расплывчатым в своем вопросе, но отчасти я ищу общий совет о том, как люди сделали UITableViews быстрее. - person Luke; 30.05.2011

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

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

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

person Nir Golan    schedule 22.06.2011
comment
Он не размещает новые изображения каждый раз, только когда создается новая ячейка. - person EmilioPelaez; 23.06.2011
comment
@Emilio .. я имел в виду именно это. Он мог использовать общий образ. Мы ничего не знаем о cell gradient2.png, это может занять 1 МБ AFAIK. - person Nir Golan; 23.06.2011
comment
Дело в следующем: 'cell.backgroundView = [[UIImageView alloc] initWithImage: [UIImage imageNamed: @cell gradient2.png]]];' приводит к утечке памяти. Изображение должно иметь точный размер / без альфа-канала для этого места. Если изображение уже используется, оно кэшируется. - person Teodor Kostov; 25.06.2011

Реализует ли делегат табличного представления:

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

В таком случае вы можете вместо этого рассмотреть возможность установки вашего свойства UITableViewCell rowHeight.

person Andrew Ebling    schedule 30.05.2011
comment
извините, какова связь между rowHeight и вялым tableView? - person Krishnabhadra; 30.05.2011
comment
@Kris Высота строки вызывается всякий раз, когда отображается ячейка. Если там делать дорогостоящие расчеты, это ухудшит производительность. - person EmilioPelaez; 23.06.2011
comment
@ Emilio .. Это означает, что @Andrew говорил о настройке rowheight непосредственно при создании ячейки, вместо реализации этого делегата .. верно? - person Krishnabhadra; 23.06.2011
comment
Действительно, реализуя этот метод, он автоматически добавляет немало затрат к рисованию / прокрутке табличного представления. Попробуйте сделать высоту строки статической, чтобы можно было исключить этот метод. Это в сочетании со многими другими ответами здесь должно оказаться чрезвычайно полезным. - person Greg Combs; 23.06.2011
comment
Это было для меня узким местом. - person Andrew; 10.12.2014
comment
Настройка tableView.estimatedRowHeight может немного помочь. На мой взгляд, с этим рендеринг стал немного быстрее, но прокрутка по-прежнему очень прерывистая. - person Andrew; 10.12.2014

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

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

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

person EmilioPelaez    schedule 21.06.2011

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

person Drew C    schedule 21.06.2011

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

Мое приложение отображает разные изображения в каждой ячейке. После добавления примерно 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)")
}

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

person Anthony Akentiev    schedule 27.06.2015

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

person Prerna    schedule 22.07.2011