iOS прокрутка одного UIScrollView на основе contentOffset другого выходит из синхронизации - PullRequest
0 голосов
/ 26 ноября 2018

РЕДАКТИРОВАТЬ: я прикрепил простую ссылку dropbox демонстрационного проекта внизу

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

Каждый элемент коллекционного просмотра имеет 150 пикселей для этой цели тестирования.

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

Я пытаюсь получить следующий интерфейс:

Начало:

enter image description here

Прокрутить представление коллекции в ячейку 1:

enter image description here

Прокрутите представление коллекции в ячейку 2:

enter image description here

Все это отлично работает первые несколько раз, но когда я прокручиваю представление коллекции назад и вперед несколькоВ разы на большие расстояния (скажем, ячейка 10 -> 0 -> 10 -> 0 и т. д.), просмотр прокрутки не синхронизируется на «крошечные» расстояния.Чтобы проиллюстрировать это, обратите внимание, что есть «желтый» цвет из второго UIView на правом краю прокрутки:

enter image description here

Я вижу этопроблема с NSLogging contentOffset просмотра scrollview (обратите внимание, как он начинает выходить из синхронизации на 0,5 после нескольких раз):

2018-11-25 19:24:28.273278-0500 ScrollViewMatchTest[19412:1203912] Finished: 0
2018-11-25 19:24:31.606521-0500 ScrollViewMatchTest[19412:1203912] Finished: 0.5
2018-11-25 19:24:55.173709-0500 ScrollViewMatchTest[19412:1203912] Finished: 1.5
2018-11-25 19:25:03.007528-0500 ScrollViewMatchTest[19412:1203912] Finished: 1.5
2018-11-25 19:25:07.841096-0500 ScrollViewMatchTest[19412:1203912] Finished: 2.5
2018-11-25 19:26:57.634429-0500

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


Обходное решение путем сброса contentOffset scrollView для закрытия, кратного ширине экрана:

-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    if (scrollView==self.myCollectionView) {
        NSLog(@"Finished: %g",self.myScrollView.contentOffset.x);
        NSLog(@"Closest: %g",RoundTo(self.myScrollView.contentOffset.x, self.myScrollView.frame.size.width));
        [self.myScrollView setContentOffset:CGPointMake(RoundTo(self.myScrollView.contentOffset.x, self.myScrollView.frame.size.width), self.myScrollView.contentOffset.y) animated:YES];
    }
}

float RoundTo(float number, float to)
{
    if (number >= 0) {
        return to * floorf(number / to + 0.5f);
    }
    else {
        return to * ceilf(number / to - 0.5f);
    }
}

КОНЕЦ РАБОЧЕГО РЕШЕНИЯ

Я приложил простую демонстрациюПроект, чтобы проиллюстрировать эту проблему также (запустите приложение и несколько раз агрессивно прокручивайте взад-вперед на верхнем свитке): https://www.dropbox.com/s/e2bzgo6abq5wmgw/ScrollViewMatchTest.zip?dl=0

Вот код:

#import "ViewController.h"
#define countOfItems 50

@interface ViewController (){
    CGFloat previousOffset_Header;
    CGFloat previousOffset_Scrollview;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.myCollectionView registerNib:[UINib nibWithNibName:@"MyCell" bundle:nil] forCellWithReuseIdentifier:@"cell"];
    [self.myCollectionView reloadData];

    for (NSInteger i=0; i<countOfItems; i++) {
        UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(i*self.myScrollView.frame.size.width, 0, self.myScrollView.frame.size.width, self.myScrollView.frame.size.height)];
        myView.backgroundColor=i%2==0?[UIColor blueColor]:[UIColor yellowColor];
        [self.myScrollView addSubview:myView];

    }
    self.myScrollView.contentSize=CGSizeMake(countOfItems*self.myScrollView.frame.size.width, self.myScrollView.frame.size.height);


}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return countOfItems;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{

    MyCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    cell.backgroundColor = indexPath.item%2==0?[UIColor blueColor]:[UIColor yellowColor];
    cell.myLabel.text=[NSString stringWithFormat:@"%ld",indexPath.item];

    return cell;
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    if (scrollView==self.myCollectionView) {
        previousOffset_Header = self.myCollectionView.contentOffset.x;
        previousOffset_Scrollview = self.myScrollView.contentOffset.x;
    }
}

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

    if (scrollView==self.myCollectionView) {
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
        [self performSelector:@selector(scrollViewDidEndScrollingAnimation:) withObject:scrollView afterDelay:0.1 inModes:@[NSRunLoopCommonModes]];
        CGFloat offsetToMoveBy = (self.myCollectionView.contentOffset.x-previousOffset_Header)*(self.myScrollView.frame.size.width/150);
        previousOffset_Scrollview = previousOffset_Scrollview +offsetToMoveBy;

        [self.myScrollView setContentOffset:CGPointMake(previousOffset_Scrollview, self.myScrollView.contentOffset.y) animated:NO];
        previousOffset_Header = self.myCollectionView.contentOffset.x;

    }
}

-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    NSLog(@"Finished: %g",self.myScrollView.contentOffset.x);
}

@end

Ответы [ 2 ]

0 голосов
/ 26 ноября 2018

Есть пара вещей, которые вы должны делать по-другому: способ вычисления смещения без использования обратных вызовов UIScrollViewDelegate scrollViewDidEndDragging:willDecelerate: и scrollViewDidEndDecelerating:.Я переписал соответствующий код:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    if (scrollView==self.myCollectionView) {
      [self calculateScrollviewOffset:self.myCollectionView];
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
  if (scrollView == self.myCollectionView && !decelerate) {
    [self calculateEndPosition: self.myCollectionView];
  }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
  if (scrollView == self.myCollectionView) {
    [self calculateEndPosition: self.myCollectionView];
  }
}

- (void) calculateScrollviewOffset:(UICollectionView *) collectionView {
  CGFloat cellWidth = 150.0;
  CGFloat percentageMoved = collectionView.contentOffset.x / cellWidth;
  [self setScrollViewOffset:percentageMoved animated:false];
}

- (void) calculateEndPosition:(UICollectionView*) collectionView {
  CGFloat cellWidth = 150.0;
  // NOTE: Add 0.5 to play around with the end animation positioning
  CGFloat percentageMoved = floor(collectionView.contentOffset.x / cellWidth);
  CGFloat collectionViewFixedOffset = (percentageMoved * cellWidth);
  [collectionView setContentOffset:CGPointMake(collectionViewFixedOffset, collectionView.contentOffset.y) animated:true];
  [self setScrollViewOffset:percentageMoved animated:true];
}

- (void) setScrollViewOffset:(CGFloat) percentageMoved animated:(BOOL) animated {
  CGFloat newOffsetX = percentageMoved * self.myScrollView.frame.size.width;
  [self.myScrollView setContentOffset:CGPointMake(newOffsetX, self.myScrollView.contentOffset.y) animated: animated];
}
0 голосов
/ 26 ноября 2018

Помните, что в viewDidLoad методе myScrollView кадр на самом деле не настроен на реальное устройство.

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

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