UIScrollView. Есть мысли о реализации "бесконечной" прокрутки / масштабирования?

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

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

Другие обдумывали эту идею? Да, я знаю, мы говорим о воссоздании Google Maps здесь. Любое понимание будет высоко ценится.

Ура, Дуг

5 ответов

Решение

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

  1. Обработка фиксированного contentSize: иметь представление фиксированного размера, обработанное представлением прокрутки, и при запуске или создании экземпляра установить смещение содержимого так, чтобы вы видели середину обработанного представления. Затем просто просмотрите смещение содержимого (используя KVO или другой метод), и, если вы приближаетесь к какому-либо краю, обновите содержимое представления новым набором содержимого (смещение соответствующим образом) и сбросьте представление прокрутки. contentOffset свойство быть в середине.
  2. Обработка масштабирования: сделайте что-то похожее, только в этот раз смотрите коэффициент масштабирования в представлении прокрутки. Всякий раз, когда он достигает определенной точки, сделайте некоторые манипуляции с любыми данными, которые вы представляете, чтобы они выглядели увеличенными, затем сбросьте коэффициент масштабирования до 1,0. Например, если вы прокручиваете изображение и оно увеличивается в два раза, программно примените какое-либо преобразование, чтобы сделать изображение в два раза больше, а затем сбросьте коэффициент масштабирования вида прокрутки до 1,0. Изображение все равно будет выглядеть увеличенным, но при просмотре с прокруткой можно будет продолжать увеличивать изображение при необходимости. (Google Maps делает еще один шаг вперед, когда лениво загружает более подробные представления по мере увеличения масштаба пользователя - вы можете или не можете реализовать это.)

Я только что закончил реализацию бесконечного свитка для меня. В моей реализации у меня есть UITableViewCell с scrollView и навигационными кнопками. ScrollView содержит x видов с одинаковой шириной. представления располагаются горизонтально, и подкачка включена.

scrollView.clipsToBounds = YES;
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
scrollView.showsHorizontalScrollIndicator = NO;

Мой кодлогика похожа на следующее:

  1. В моей функции инициализации я
    • создать все виды (для вида прокрутки) и
    • положить их в массив и
    • добавить их в scrollView
  2. Затем я вызываю функцию, которая вычисляет в цикле позиции для каждого вида (каждый раз, когда вы обнаруживаете прокрутку, эту функцию тоже нужно будет вызывать). Он всегда берет первый элемент массива и устанавливает фрейм в (0,0,...,...), второй с (i*width,0,....,....) и т. Д. на. Вызываемая функция выглядит так:

    - (void)updateOffsetsOfViews{
        int xpos = 0;
        for (int i=0; i<[views count]; i++) {
            UIImageView *_view = [views objectAtIndex:i];
            CGRect aFrame = _view.frame;
            aFrame.origin.x = xpos;
            aFrame.origin.y = 0.0;
            _view.frame = aFrame;
            xpos += viewWidth;
        }
        float center = 0;
        if(fmod([views count],2) == 1){
            center = viewWidth * ([views count]-1)/2;
        }else {
            center = viewWidth * [views count]/2;
        }
        [scrollView setContentOffset:CGPointMake(center, 0)];
        lastOffset = center;
    }
    
  3. Затем (все еще в процессе инициализации) я добавляю наблюдателя

    [scrollView addObserver:self forKeyPath:@"contentOffset" options:0 context:nil];
    

    поэтому каждый раз, когда что-то изменяется в scrollView, я вызываю функцию (наблюдаю за именем): она выглядит так:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object  change:(NSDictionary *)change context:(void *)context
    {
        UIImageView *_viewFirst = (UIImageView *)[views objectAtIndex:0];
        if ( fmod([scrollView contentOffset].x,viewWidth)  == 0.0) {
            if ([scrollView contentOffset].x > lastOffset) {
                [views removeObjectAtIndex:0];
                [views addObject:_viewFirst];                
                [self updateOffsetsOfViews];    
            }else if ([scrollView contentOffset].x < lastOffset) {
                UIImageView *_viewLast = (UIImageView *)[views lastObject];
                [views removeLastObject];
                [views insertObject:_viewLast atIndex:0];            
                [self updateOffsetsOfViews];
            }
        }
    }
    
  4. А в dealloc или viewDidUnload (зависит от того, как вы это реализуете) не забудьте удалить наблюдателя.

    [scrollView removeObserver:self forKeyPath:@"contentOffset"];   
    

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

Пример проекта StreetScroller от Apple демонстрирует, как выполнять бесконечную прокрутку в UIScrollView.

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

Возможно постановка contentSize до некоторого гигантского значения, а затем перемещая ограниченное количество базовых видов вокруг, чтобы отследить позицию вида, как в примере Tiling, добьется цели.

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

Во всяком случае, это то, что я собираюсь попробовать.

Другие вопросы по тегам