мне нужна помощь с реализацией UIScrollView со следующими требованиями:Paged UIScrollView с ленивой задержкой загрузкой
1) pagingEnabled = верно.
2) загрузка ленивых страниц.
3) страницы загружаются в фоновом режиме. Поэтому мне нужно сначала запустить загрузку страницы X, затем получить уведомление о том, что страница полностью загружена и только затем разрешить пользователю прокручивать ее.
4) возможность изменения текущей страницы.
Моя первая попытка состояла в том, чтобы переопределить scrollViewDidEndDeacelerating и scrollViewDidScroll, но у меня были проблемы с удержанием на половине страниц (когда вы останавливаете прокрутку на половине страницы, а затем ждете добавления новой страницы в свиток) и пустые страницы (когда пользователь прокручивается слишком быстро).
Моя вторая попытка состояла в том, чтобы переопределить метод layoutSubviews UIScrollView и выполнить все вычисления там. Но это кажется очень сложным.
Итак, я хотел бы найти примеры подобных реализаций.
Теперь у меня есть такой код:
Я реализовал scrollViewWillBeginDragging и scrollViewDidEndDecelerating:
- (void)scrollViewWillBeginDragging:(UIScrollView *)aScrollView
{
isScrolling = YES;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView
{
isScrolling = NO;
// Here we load the page that was prepared when the user was scrolling
if (needDisplay > -1) {
//NSLog(@"Loading queued page %d", needDisplay);
[self loadScrollViewWithPage:needDisplay forceAddToSuperview:NO animated:YES];
needDisplay = -1;
}
// minLoadedPageIndex - index of the first loaded page.
int selectedIndex = MIN(floor((aScrollView.contentOffset.x - pageWidth/2)/pageWidth) + 1 + minLoadedPageIndex, [photoItems count] - 1);
[self loadScrollViewWithPage:selectedIndex - 1 forceAddToSuperview:NO animated:YES];
[self loadScrollViewWithPage:selectedIndex forceAddToSuperview:NO animated:YES];
[self loadScrollViewWithPage:selectedIndex + 1 forceAddToSuperview:NO animated:YES];
}
В loadScrollViewWithPage Я создаю вид контроллера страницы, который загружает данные с сервера в фоновом режиме. Я не добавляю представление в scrollview, пока он не загрузит данные с сервера.
- (void)loadScrollViewWithPage:(int)page forceAddToSuperview:(BOOL)value animated:(BOOL)animated
{
DetailController *controller = page >= viewControllers.count ? [NSNull null] :[viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{
controller = [[DetailController alloc] initWithNibName:@"DetailController" bundle:nil];
controller.delegate = self;
controller.view.hidden = NO; //this will call viewDidLoad.
if (page >= viewControllers.count) {
[viewControllers addObject:controller];
}
else {
[viewControllers replaceObjectAtIndex:page withObject:controller];
}
[controller release];
}
// add the controller's view to the scroll view
if (controller.view && controller.view.superview == nil && (controller.isLoadedOrFailed || value)) {
[self setNumberOfVisiblePages:visiblePagesCount+1];
if (page < selectedIndex) {
// We are adding the page to the left of the current page,
// so we need to adjust the content offset.
CGFloat offset = (int)scrollView.contentOffset.x % (int)scrollView.frame.size.width;
offset = scrollView.frame.size.width * (selectedIndex - minLoadedPageIndex) + offset;
[scrollView setContentOffset:CGPointMake(offset, 0.0) animated:animated];
}
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * (page - minLoadedPageIndex);
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
[controller viewWillAppear:NO];
}
}
Также у меня есть метод detailControllerDidFinishDownload который вызывается, когда данные для контроллера представления страницы были загружены.
- (void)detailControllerDidFinishDownload:(DetailController *)controller
{
... //here I calculate new minLoadedPageIndex value
// reset pages frames
if (minLoadedPageIndex < oldMinPage) {
for(int i=oldMinPage;i < [viewControllers count]; i++) {
DetailController *detailsController = [viewControllers objectAtIndex:i];
if ((NSNull *)detailsController != [NSNull null]) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * (i - minLoadedPageIndex);
frame.origin.y = 0;
[detailsController.view setFrame:frame];
}
}
}
// load the page now or delay load until the scrolling will be finished
if (!isScrolling) {
[self loadScrollViewWithPage:[photoItems indexOfObject:controller.photoItem] forceAddToSuperview:NO animated:NO];
}
else {
needDisplay = [photoItems indexOfObject:controller.photoItem];
//NSLog(@"pageControlUsed is used!!! %d", [photoItems indexOfObject:controller.photoItem]);
}
}
Проблема у меня сейчас является то, что иногда прокрутки заклинивание на середине (или где-то вблизи середины) страниц, и это не будет идти до ближайшей страницы отскока пока я чуть больше его. Мои тесты показали, что эта ситуация возникает, если я прокручиваю рамку просмотра контента (на экране отображается прокрутка) и дождитесь загрузки новой страницы. В 1 из 10 раз прокручивается свиток.
Спасибо, Михаил!
Я отбросил требование, чтобы пользователь не мог прокручивать страницы во время загрузки; показание индикатора прогресса кажется гораздо более удобным для пользователя и не требует нарушения поведения UIScrollView почти так же. –
Мой клиент хочет этого, и я не могу убедить его отказаться от него :( – MrZoidberg