Разрыв между страницами в стиле приложения для фотографий в UIScrollView с помощью pagingEnabled

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

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

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


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

Я автор статьи ScrollingMadness + пример, на который я ссылался здесь. Я реализовал прогамматическое масштабирование и получил масштабирование в фотографии + прокрутка, работающие вместе с постраничным переходом между фотографиями. Так что я знаю, как играть с UIScrollView, и ищу продвинутые вещи.

Пожалуйста, не указывайте мне на TTScrollView. Я уже указывал на это многим, но считаю, что это слишком далеко от нативного поведения UIScrollView, и я не хочу использовать его в своих проектах.

8 ответов

Решение

Обратите внимание, что этот ответ довольно старый. Основная концепция все еще работает, но вы не должны жестко кодировать размеры представлений в iOS7 и 8. Даже если вы игнорируете этот совет, вы не должны использовать 480 или 330.

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

#define kViewFrameWidth 330; // i.e. more than 320

CGRect scrollFrame;
scrollFrame.origin.x = 0;
scrollFrame.origin.y = 0; 
scrollFrame.size.width = kViewFrameWidth;
scrollFrame.size.height = 480;

UIScrollView* myScrollView = [[UIScrollView alloc] initWithFrame:scrollFrame];
myScrollView.bounces = YES;
myScrollView.pagingEnabled = YES;
myScrollView.backgroundColor = [UIColor redColor];

UIImage* leftImage = [UIImage imageNamed:@"ScrollTestImageL.png"];
UIImageView* leftView = [[UIImageView alloc] initWithImage:leftImage];
leftView.backgroundColor = [UIColor whiteColor];
leftView.frame = CGRectMake(0,0,320,480);

UIImage* rightImage = [UIImage imageNamed:@"ScrollTestImageR.png"];
UIImageView* rightView = [[UIImageView alloc] initWithImage:rightImage];
rightView.backgroundColor = [UIColor blackColor];
rightView.frame = CGRectMake(kViewFrameWidth * 2,0,320,480);

UIImage* centerImage = [UIImage imageNamed:@"ScrollTestImageC.png"];
UIImageView* centerView = [[UIImageView alloc] initWithImage:centerImage];
centerView.backgroundColor = [UIColor grayColor];
centerView.frame = CGRectMake(kViewFrameWidth,0,320,480);

[myScrollView addSubview:leftView];
[myScrollView addSubview:rightView];
[myScrollView addSubview:centerView];

[myScrollView setContentSize:CGSizeMake(kViewFrameWidth * 3, 480)];
[myScrollView setContentOffset:CGPointMake(kViewFrameWidth, 0)];

[leftView release];
[rightView release];
[centerView release];

Извиняюсь, если это не скомпилировано, я протестировал его в альбомном приложении и отредактировал его обратно в портрет. Я уверен, что вы поняли, хотя. Он опирается на отсечение superview, которое всегда будет иметь место для полноэкранного просмотра.

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

Если вы используете UIScrollView в viewController, который является частью UINavigationController, контроллер навигации изменит размер кадра вашего scrollView.

То есть у вас есть приложение, которое использует UINavigationController для переключения между различными представлениями. Вы нажимаете viewController, у которого есть scrollView, и вы создаете этот scrollView в методе viewinontroller -init. Вы назначаете ему кадр (0, 0, 340, 480).

Теперь перейдите к методу -viewDidAppear вашего viewController, получите кадр созданного вами scrollView. Вы увидите, что ширина была уменьшена до 320 пикселей. Таким образом, пейджинг не будет работать правильно. Вы ожидаете, что scrollView переместится на 340 пикселей, но вместо этого он переместится на 320.

UINavigationController немного печально известен тем, что возился с подпредставлениями. Он перемещает их и изменяет их размеры для размещения панели навигации. Короче говоря, это не командный игрок - особенно в этом случае. В других местах в Интернете рекомендуется не использовать UINavigationController, если вам нужен точный контроль размера и местоположения ваших представлений. Вместо этого они предлагают создать собственный класс navigationController на основе UINavigationBar.

Ну, это тонна работы. К счастью, есть более простое решение: установите рамку scrollView в методе -viewDontAppear вашего viewController. На этом этапе UINavigationController завершает работу с кадром, поэтому вы можете сбросить его до того, каким он должен быть, и scrollView будет вести себя правильно.

Это актуально для OS 3.0. Я не проверял 3.1 или 2.2.1. Я также подал в Apple отчет об ошибке, предложив, чтобы они изменили UINavigationController с помощью BOOL, такого как "-shouldAutoarrangeSubviews", чтобы мы могли сохранить этот класс своими грязными руками от подпредставлений.

Пока это не произойдет, приведенное выше исправление даст вам пробелы в разбивке по страницам UIScrollView внутри UINavigationController.

Apple выпустила видео сессий WWDC 2010 для всех участников программы для разработчиков iphone. Одна из обсуждаемых тем - как они создали приложение для фотографий!!! Они шаг за шагом создают очень похожее приложение и сделали весь код доступным бесплатно.

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

http://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?code=y&source=x&bundleID=20645

И вот ссылка на страницу iTunes WWDC:

http://insideapple.apple.com/redir/cbx-cgi.do?v=2&la=en&lc=&a=kGSol9sgPHP%2BtlWtLp%2BEP%2FnxnZarjWJglPBZRHd3oDbACudP51JNGS8KlsFgxZto9X%2BTsnqSbeUSWX0doe%2Fzv%2FN5XV55%2FomsyfRgFBysOnIVggO%2Fn2p%2BiweDK%2F%2FmsIXj

Способ сделать это, как вы сказали, сочетание нескольких вещей.

Если вы хотите промежуток между изображениями в 20 пикселей, вам необходимо:

Сначала увеличьте общую ширину вида прокрутки на 20 пикселей и переместите влево на 10 пикселей.

Во-вторых, когда вы размещаете xLoc своих изображений, добавьте 20 пикселей для каждого изображения, чтобы они были на расстоянии 20 пикселей друг от друга. В-третьих, установите начальный xLoc ваших изображений на 10 пикселей вместо 0 пикселей.

В-четвертых, убедитесь, что вы установили размер содержимого для просмотра с прокруткой, чтобы добавить 20 пикселей для каждого изображения. Так что, если у вас есть изображения kNumImages, и каждое из них - kScrollObjWidth, то вы идете так:

[scrollView setContentSize:CGSizeMake((kNumImages * (kScrollObjWidth+20)),  kScrollObjHeight)];

Это должно работать после этого!

Я просто подумал, что добавлю сюда для потомков решение, которое я в итоге выбрал. В течение долгого времени я использовал решение Брайана по настройке кадра в -viewDidAppearи это сработало блестяще. Однако, поскольку iOS представила многозадачность, я столкнулся с проблемой, когда рамка просмотра прокрутки изменяется, когда приложение возобновляет работу из фона. В этом случае, -viewDidAppear не вызывался, и я не смог найти метод делегата, который был бы вызван в нужное время, чтобы отменить изменение. Поэтому я решил сделать мой вид прокрутки подвидом моего View Viewler, и это, похоже, решило проблему. Это также имеет то преимущество, что нет необходимости использовать -viewDidAppear изменить кадр - вы можете сделать это сразу после создания прокрутки. Мой вопрос здесь содержит детали, но я также опубликую их здесь:

CGRect frame = CGRectMake(0, 0, 320, 460);
scrollView = [[UIScrollView alloc] initWithFrame:frame];

// I do some things with frame here

CGRect f = scrollView.frame;
f.size.width += PADDING; // PADDING is defined as 20 elsewhere
scrollView.frame = f;

[self.view addSubview:scrollView];

Может быть, вы хотите попробовать свойство contentInset UIScrollView?

myScrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 10.0);

Это всего лишь догадка, поэтому извиняюсь, если полностью ошибаюсь, но возможно ли, что contentSize просто установлен немного шире, чем ширина экрана.

Правильная информация затем отображается в представлении ширины экрана, а UIScrollView позаботится обо всем остальном?

Чтобы избежать путаницы с фреймом UIScrollView, вы можете создать подкласс UIScrollView и переопределить layoutSubviews, чтобы применить смещение к каждой странице.

Идея основана на следующих наблюдениях:

  1. Когда zoomScale!=1, смещение равно нулю, когда оно на левом / правом краю
  2. Когда zoomScale ==1, смещение равно нулю, когда оно находится в видимом центре прямоугольника

Затем получается следующий код:

- (void) layoutSubviews
{
    [super layoutSubviews];

    // Find a reference point to calculate the offset:
    CGRect bounds = self.bounds;
    CGFloat pageGap = 8.f;
    CGSize pageSize = bounds.size;
    CGFloat pageWidth = pageSize.width;
    CGFloat halfPageWidth = pageWidth / 2.f;
    CGFloat scale = self.zoomScale;
    CGRect visibleRect = CGRectMake(bounds.origin.x / scale, bounds.origin.y / scale, bounds.size.width / scale, bounds.size.height / scale);
    CGFloat totalWidth = [self contentSize].width / scale;
    CGFloat scrollWidth = totalWidth - visibleRect.size.width;
    CGFloat scrollX = CGRectGetMidX(visibleRect) - visibleRect.size.width / 2.f;
    CGFloat scrollPercentage = scrollX / scrollWidth;
    CGFloat referencePoint = (totalWidth - pageWidth) * scrollPercentage + halfPageWidth;

    // (use your own way to get all visible pages, each page is assumed to be inside a common container)
    NSArray * visiblePages = [self visiblePages];

    // Layout each visible page:
    for (UIView * view in visiblePages)
    {
        NSInteger pageIndex = [self pageIndexForView:view]; // (use your own way to get the page index)

        // make a gap between pages
        CGFloat actualPageCenter = pageWidth * pageIndex + halfPageWidth;
        CGFloat distanceFromRefPoint = actualPageCenter - referencePoint;
        CGFloat numOfPageFromRefPoint = distanceFromRefPoint / pageWidth;
        CGFloat offset = numOfPageFromRefPoint * pageGap;
        CGFloat pageLeft = actualPageCenter - halfPageWidth + offset;

        view.frame = CGRectMake(pageLeft, 0.f, pageSize.width, pageSize.height);
    }
}
Другие вопросы по тегам