23

分页模式下的 UIScrollView 假定页面彼此相邻,没有间隙。但是,如果您在“照片”应用程序中打开一张照片并在照片中滑动,您会发现它在页面之间存在一些间隙。我也想要这些差距。

我正在寻找现有的解决方案(如果有的话),或者除了我在下面解释的那个之外,还有一些关于实现页面间隙的更奇怪的想法。或者也许我错过了一些明显的简单方法?

需要明确的是:我希望只有在滚动时才能看到间隙,所以我不能简单地插入页面内容。


我的计划是尝试从scrollViewDidScroll回调内部移动页面内容,以便(假设您向右滚动)最初目标页面稍微偏移到其页面边界的右侧,并且当您到达目标页面时它回到了正确的位置,并且源页面稍微偏离了其边界的左侧。(或者也许不是连续移动东西,我会更好地移动偏移量,比如说,正好在页面之间的中间。)

我是ScrollingMadness 文章+示例的作者,我一直在向这里推荐一些人。我已经实现了程序缩放,并让照片内缩放+滚动与照片间分页一起工作。所以我知道如何使用 UIScrollView,并且正在寻找高级的东西。

请不要将我指向 TTScrollView。我自己已经向很多人指出了它,但我认为它与本机 UIScrollView 行为相去甚远,并且不想在我的项目中使用它。

4

8 回答 8

27

请注意,这个答案已经很老了。基本概念仍然有效,但您不应该在 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];

抱歉,如果无法编译,我在横向应用程序中对其进行了测试,然后将其手动编辑回纵向。我相信你明白了。它依赖于超级视图剪辑,对于全屏视图总是如此。

于 2009-05-12T05:56:54.130 回答
22

所以我没有足够的“代表”来对上面的答案发表评论。这个答案是正确的,但有一个大问题需要注意:

如果您在作为 UINavigationController 一部分的 viewController 中使用 UIScrollView,则导航控制器将调整您的 scrollView 框架的大小。

也就是说,您有一个使用 UINavigationController 在不同视图之间切换的应用程序。您推送一个具有滚动视图的视图控制器,并在视图控制器的 -init 方法中创建此滚动视图。您为其分配一个 (0, 0, 340, 480) 的帧。

现在,转到 viewController 的 -viewDidAppear 方法,获取您创建的 scrollView 的框架。您会发现宽度已减小到 320 像素。因此,分页将无法正常工作。您会期望 scrollView 移动 340 像素,但它会移动 320 像素。

UINavigationController 因弄乱子视图而臭名昭著。它移动它们并调整它们的大小以适应导航栏。简而言之,它不是团队合作者——尤其是在这种情况下。如果您需要精确控制视图的大小和位置,网络上的其他地方建议您不要使用 UINavigationController。相反,他们建议您基于 UINavigationBar 创建自己的 navigationController 类。

嗯,这是一大堆工作。幸运的是,有一个更简单的解决方案:在 viewController 的 -viewDidAppear 方法中设置 scrollView 的框架。此时,UINavigationController 已经完成了对框架的处理,因此您可以将其重置为应有的状态,并且滚动视图将正常运行。

这与 OS 3.0 有关。我没有测试过 3.1 或 2.2.1。我还向 Apple 提交了一份错误报告,建议他们使用诸如“-shouldAutoarrangeSubviews”之类的 BOOL 修改 UINavigationController,以便我们可以使该类保持其肮脏的手离开子视图。

在此之前,上面的修复将在 UINavigationController 内的分页 UIScrollView 中为您提供间隙。

于 2009-07-18T01:40:01.987 回答
7

Apple 已向 iphone 开发者计划的所有成员发布了 2010 年 WWDC 会议视频。讨论的主题之一是他们如何创建照片应用程序!!!他们逐步构建了一个非常相似的应用程序,并免费提供了所有代码。

它也不使用私有 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

于 2010-06-20T19:39:42.420 回答
4

做到这一点的方法就像你说的,结合了几件事。

如果您希望图像之间有 20 像素的间隙,则需要:

首先,将滚动视图的总宽度扩大 20 像素,然后将其向左移动 10 像素。

其次,当您布置图像的 xLoc 时,为每个图像添加 20px,使它们相隔 20px。第三,将图像的初始 xLoc 设置为 10px 而不是 0px。

第四,确保将滚动视图的内容大小设置为为每个图像添加 20 像素。因此,如果您有 kNumImages 图像并且每个图像都是 kScrollObjWidth,那么您可以这样:

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

它应该在那之后工作!

于 2009-06-12T15:53:08.807 回答
0

这只是一种预感,如果完全错误,请道歉,但是 contentSize 是否可能只是设置为比屏幕宽度略宽。

然后在视图中将正确的信息呈现到屏幕宽度,而 UIScrollView 会负责其余的工作吗?

于 2009-05-11T19:04:11.683 回答
0

也许你想试试 UIScrollView 的 contentInset 属性?

myScrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 10.0);
于 2009-05-11T22:28:07.263 回答
0

我只是想我会在这里添加我最终使用的解决方案。很长一段时间以来,我一直在使用 Bryan 调整框架的解决方案,-viewDidAppear效果非常好。但是,自从 iOS 引入多任务处理以来,我一直遇到一个问题,即当应用程序从后台恢复时,滚动视图框架会发生变化。在这种情况下,-viewDidAppear没有被调用,并且我找不到将在正确的时间调用以撤消更改的委托方法。所以我决定让我的滚动视图成为我的视图控制器视图的子视图,这似乎解决了这个问题。这还具有不需要使用-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];
于 2011-12-30T14:11:18.930 回答
0

为了避免弄乱 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);
    }
}
于 2014-04-30T04:10:10.377 回答