Прокрутка Бога UIScrollView

16
задан Ben Robinson 7 August 2010 в 11:35
поделиться

7 ответов

Возможно, что то, что устанавливает эти числа там, не очень впечатляет, когда вы устанавливаете contentOffset в его руки. Таким образом, он просто устанавливает то, что, по его мнению, должно быть contentOffset в следующий момент, не проверяя, изменилось ли за это время contentOffset.

Я бы создал подкласс UIScrollView и вложил волшебство в метод setContentOffset . По моему опыту, все изменение смещения содержимого проходит через этот метод, даже изменение смещения содержимого, вызванное внутренней прокруткой. Просто выполните [super setContentOffset: ..] в какой-то момент, чтобы передать сообщение реальному UIScrollView.

Может быть, если вы поместите туда свое переключение, это сработает лучше. Вы могли бы по крайней мере обнаружить 3000-off настройку contentOffset и исправить ее перед передачей сообщения. Если вы также переопределите метод contentOffset , вы можете попробовать и посмотреть, сможете ли вы создать виртуальный бесконечный размер контента и уменьшить его до реальных пропорций «под капотом».

7
ответ дан 30 November 2019 в 16:13
поделиться

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

Вместо 3 изображений я настроил представление прокрутки с 5 изображениями, например:

3 | 1 | 2 | 3 | 1

и рассчитал представление содержимого соответствующим образом тогда как размер контента фиксирован. Вот код:

for ( NSUInteger i = 1; i <= NO_OF_IMAGES_IN_SCROLLVIEW + 2 ; i ++) {

    UIImage *image;
    if ( i  % 3 == 1){

        image = [UIImage imageNamed:[NSString stringWithFormat:@"img1.png"]];
    }

    else if (i % 3 == 2 ) {

        image = [UIImage imageNamed:[NSString stringWithFormat:@"img2.png"]];

    }

    else {

        image = [UIImage imageNamed:[NSString stringWithFormat:@"img3.png"]];
    }

    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake((i-1)*_scrollView.frame.size.width, 0, _scrollView.frame.size.width, _scrollView.frame.size.height)];
    imageView.contentMode=UIViewContentModeScaleToFill;
    [imageView setImage:image];
    imageView.tag=i+1;
    [self.scrollView addSubview:imageView];
}

[self.scrollView setContentOffset:CGPointMake(self.scrollView.frame.size.width, 0)];
[scrMain setContentSize:CGSizeMake(self.scrollView.frame.size.width * ( NO_OF_IMAGES_IN_SCROLLVIEW + 2 ), self.scrollView.frame.size.height)];

Теперь, когда изображения добавлены, остается только создать иллюзию бесконечной прокрутки. Для этого я «телепортировал» пользователя в три основных изображения, когда бы он ни пытался прокрутить одно из двух внешних изображений. Важно сделать это без анимации, чтобы пользователь не смог это почувствовать:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    if (scrollView.contentOffset.x < scrollView.frame.size.width ){

        [scrollView scrollRectToVisible:CGRectMake(scrollView.contentOffset.x + 3 * scrollView.frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height) animated:NO];
    }
}

    else if ( scrollView.contentOffset.x > 4 * scrollView.frame.size.width  ){

        [scrollView scrollRectToVisible:CGRectMake(scrollView.contentOffset.x - 3 * scrollView.frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height) animated:NO];
    }
}
6
ответ дан 30 November 2019 в 16:13
поделиться

Я сделал пример проекта, чтобы решить вашу проблему.

Вы можете скачать код с

https://code.google.com/p/iphone-infinite-horizont-scroller/

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

5
ответ дан 30 November 2019 в 16:13
поделиться

Я разработал этот вид uiscrollview, так что вы можете проверить мой проект в Infinite UIScrollView в обоих направлениях

1
ответ дан 30 November 2019 в 16:13
поделиться

Я сделал подкласс UIScrollView, который просто делает то, что вы хотите. Он просто прокручивается навсегда в любых направлениях, даже в обоих направлениях одновременно. Он поддерживает плавную прокрутку и прокрутку подкачки

https://github.com/vasvf/NPInfiniteScrollView

1
ответ дан 30 November 2019 в 16:13
поделиться

Таким образом, одна проблема заключается в том, что установка contentOffset в методе scrollViewDidScroll: Delegate приведет к повторному запуску метода делегата, пока вы находитесь внутри него. Так что я делаю, это удаляю делегата> setContentOffset> снова устанавливаем делегата.

Вот мой код:

#import "ViewController.h"

@interface ViewController () <UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (strong, nonatomic) NSMutableArray *labels;
@property (weak, nonatomic) UILabel *originLabel;

- (void)addScrollViewLabels;
- (void)adjustOrigins:(float)deltaX;

@end


@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // just some starting size
    CGSize size = self.scrollView.frame.size;
    CGSize contentSize = CGSizeMake(size.width * 4, size.height);
    [self.scrollView setContentSize:contentSize];

    // just some starting offset
    CGPoint contentOffset = CGPointMake((contentSize.width / 2), 0);
    [self.scrollView setContentOffset:contentOffset];

    [self addScrollViewLabels];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGSize contentSize = scrollView.contentSize;
    CGSize size = scrollView.frame.size;
    CGPoint contentOffset = scrollView.contentOffset;

    const float kContentOffsetBuffer = 100;
    const float kContentSizeGrowth = (4 * size.width);

    BOOL shouldGrowContentSize = (contentOffset.x > (contentSize.width - size.width - kContentOffsetBuffer))
                                || (contentOffset.x < (kContentOffsetBuffer));
    if (shouldGrowContentSize) {
        // the contentOffset has reached a point where we need to grow the contentSize
        CGSize adjustedContentSize = CGSizeMake(contentSize.width + kContentSizeGrowth, contentSize.height);
        [self.scrollView setContentSize:adjustedContentSize];

        if(contentOffset.x < (kContentOffsetBuffer)) {
            // the growth needs to happen on the left side which means that we need to adjust the contentOffset to compensate for the growth.
            // this is not necessary when growth happens to the right since the contentOffset is the same.
            CGPoint adjustedContentOffset = CGPointMake(contentOffset.x + kContentSizeGrowth, contentOffset.y);
            [self.scrollView setDelegate:nil];
            [self.scrollView setContentOffset:adjustedContentOffset];
            [self.scrollView setDelegate:self];
            [self adjustOrigins:kContentSizeGrowth];
        }

        [self addScrollViewLabels];
    }
}


- (void)addScrollViewLabels {
    const float kOriginY = 100;

    if (!self.labels) {
        self.labels = [NSMutableArray array];
        float originX = [self.scrollView contentOffset].x;
        UILabel *label0 = [[UILabel alloc] initWithFrame:CGRectMake(originX, kOriginY, 100, 30)];
        label0.text = @"0";
        [self.scrollView addSubview:label0];
        [self.labels addObject:label0];
        self.originLabel = label0;
    }

    CGSize contentSize = [self.scrollView contentSize];
    const float kIncrementAmount = 75;
    NSInteger indexOfOriginLabel = [self.labels indexOfObject:self.originLabel];

    // add labels to the right
    UILabel *lastLabel = [self.labels lastObject];
    float lastOriginX = lastLabel.frame.origin.x;
    for (float x = (lastOriginX + kIncrementAmount); (x < (contentSize.width)) ; x += kIncrementAmount) {
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x, kOriginY, 100, 30)];
        NSInteger indexFromOrigin = ([self.labels count] - indexOfOriginLabel);
        label.text = [@(indexFromOrigin) description];
        [self.scrollView addSubview:label];
        [self.labels addObject:label];
        [label setNeedsDisplay];
    }

    // add labels to the left
    UILabel *firstLabel = [self.labels firstObject];
    float firstOriginX = firstLabel.frame.origin.x;
    for (float x = (firstOriginX - kIncrementAmount); (x >= 0) ; x -= kIncrementAmount) {
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x, kOriginY, 100, 30)];
        NSInteger indexFromOrigin = -(indexOfOriginLabel + 1);
        label.text = [@(indexFromOrigin) description];
        [self.scrollView addSubview:label];
        [self.labels insertObject:label atIndex:0];
        indexOfOriginLabel++;
        [label setNeedsDisplay];
    }
}

- (void)adjustOrigins:(float)deltaX {
    for (UILabel *label in self.labels) {
        CGRect frame = label.frame;
        frame.origin.x += deltaX;
        [label setFrame:frame];
        [label setNeedsDisplay];
    }
}


@end
0
ответ дан 30 November 2019 в 16:13
поделиться

(надеюсь, я публикую это правильно - я здесь новенький!?)

mvds был на высоте - спасибо - подкласс UIScrollView работает отлично - мне еще предстоит реализовать смещение и загрузка новых данных, но у меня есть UIScrollView, который прокручивается в бесконечном цикле!

Вот код:

#import "BRScrollView.h"

@implementation BRScrollView

- (id)awakeFromNib:(NSCoder *)decoder {
    offsetAdjustment = 0;
    [super initWithCoder:decoder];
    return self;
}

- (void)setContentOffset:(CGPoint)contentOffset {

    float realOffset = contentOffset.x + offsetAdjustment;

    //This happens when contentOffset has updated correctly - there is no need for the adjustment any more
    if (realOffset < expectedContentOffset-2000 || realOffset > expectedContentOffset+2000) {
        offsetAdjustment = 0;
        realOffset = contentOffset.x;
    }

    float pageNumber = realOffset / 320;
    float pageCount = self.contentSize.width / 320;

    if (pageNumber > pageCount-4) {
        offsetAdjustment -= 3200;
        realOffset -= 3200;
    }

    if (pageNumber < 4) {
        offsetAdjustment += 3200;
        realOffset += 3200; 
    }

    //Save expected offset for next call, and pass the real offset on
    expectedContentOffset = realOffset;     
    [super setContentOffset:CGPointMake(realOffset, 0)];


}

- (void)dealloc {
    [super dealloc];
}


@end

Примечания:

Если вам действительно нужен бесконечный цикл, вам нужно отрегулировать числа - этот код замечает, когда вы приближаетесь к краю, и перемещает вас сразу за середину

My contentSize - 6720 (21 страница)

Меня интересует только горизонтальная прокрутка, поэтому сохраняйте только значения x и жестко закодируйте y на 0!

Бен

5
ответ дан 30 November 2019 в 16:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: