【问题标题】:Photos app-like gap between pages in UIScrollView with pagingEnabled使用 pagingEnabled 的 UIScrollView 中页面之间的照片应用程序式间隙
【发布时间】:2010-10-25 08:44:21
【问题描述】:

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

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

明确一点:我希望间隙仅在滚动时可见,因此我不能简单地插入页面内容。


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

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

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

【问题讨论】:

  • ScrollingMadness 的伟大开始!我希望在放大图像时分页能够正常工作。似乎没有人能够以与照片应用程序类似的方式弄清楚如何做到这一点。 Three20 接近,但缩放图像的滚动性能很差。就像苹果刚刚制作了他们的照片应用程序来让每个人都发疯一样!就好像他们故意遗漏了某些 API 以防止轻松复制​​和改进他们的应用程序。感谢您的帮助。
  • Jonah,最新版本的 iPhone OS(甚至可能从 3.0 开始) UIScrollView 支持嵌套,它的工作方式几乎与 Phone.app 完全相同(现在差别真的很小,这是 Apple 的标准,每个人都知道)也应该遵守,而不是试图复制过时的 Phone.app)。所以继续吧,使用外部滚动视图进行分页,使用内部滚动视图进行缩放/滚动,这样就可以了。
  • 试试这个实现分页和网页图片下载github.com/aliseymen/PagedScrollView

标签: iphone cocoa-touch uiscrollview


【解决方案1】:

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

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

【讨论】:

  • 嗯。非常感谢。有趣的是,我在对另一个答案的评论中描述了同样的事情,说“我可以使滚动视图更宽以使页面更宽,但是滚动条会稍微偏离屏幕,这很难看” .但是,感谢您的帖子,我再次考虑了它,并意识到在分页模式下我不需要滚动条。愚蠢的愚蠢的我。再次感谢,当我添加这个技巧时,我会在 ScrollingMadness 示例中感谢您。
  • 谢谢,我还删除了我略带虚假的第一个答案。很高兴它成功了。
  • 简单说明一下 Andrey 的评论:您可以使用 UIScrollView 的 scrollIndicatorInsets 属性移动滚动指示器。
  • 使滚动视图大于屏幕是天才。谢谢
【解决方案2】:

为避免弄乱 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);
    }
}

【讨论】:

    【解决方案3】:

    我只是想我会在这里添加我最终采用的解决方案。很长一段时间以来,我一直在使用 Bryan 在-viewDidAppear 中调整框架的解决方案,效果非常好。但是,自从 iOS 引入多任务处理以来,我一直遇到一个问题,即当应用程序从后台恢复时,滚动视图框架会发生变化。在这种情况下,-viewDidAppear 没有被调用,我找不到可以在正确的时间调用以撤销更改的委托方法。所以我决定让我的滚动视图成为我的视图控制器视图的子视图,这似乎解决了这个问题。这还具有不需要使用-viewDidAppear 来更改框架的优点 - 您可以在创建滚动视图后立即执行此操作。我的问题here 有详细信息,但我也会在这里发布:

    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];
    

    【讨论】:

      【解决方案4】:

      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

      【讨论】:

      • 是的,UIScrollViews 中的 UIScrollViews,简单,不错。
      • 仅供参考:这是 WWDC 2010 会议 104 使用滚动视图设计应用程序——以防万一链接停止工作。
      【解决方案5】:

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

      如果您在作为 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 中给您提供空白。

      【讨论】:

      • 布莱恩,感谢您的警告。就个人而言,我认为 UINavigationController 调整我的视图大小以适应例如界面旋转或通话状态栏。所以我更喜欢的方法是让我的 UIScrollView 作为使用 UINavigationController 管理的通用 UIView 的子级。这样我仍然可以控制定位,但可以利用自动调整大小。
      • 你刚刚结束了 4 个小时的挫败感,又为我节省了 8 个小时。如果可以的话,我会投票两次。这在 OS 4.0 中仍然是一个问题。
      • 我已经为这个问题花了几个小时,thanx bryan,这是一个很好的评论,我真的很感激
      • 这是我生命中的一个小时,我再也回不来了。希望我在开始之前看到这个答案 - 谢谢!在 iOS6 中仍然相关...
      • 查看我对原始答案的编辑。基本概念应该仍然有效,但它是为 iOS2 编写的。如今,您不应该在这种情况下硬编码视图大小。
      【解决方案6】:

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

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

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

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

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

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

      之后应该可以工作了!

      【讨论】:

      • 保罗,恐怕你来晚了:这已经被建议并且确实有效,但还是谢谢。
      • 我注意到了一个问题。如果滚动视图的大小小于屏幕的完整大小,那么当您滚动页面时,您会注意到页面超出了指定的宽度。如果您的滚动视图小于整个屏幕宽度,这也是一个问题。如果有人有任何解决方法,听到他们的声音会很有帮助。
      • 剪贴蒙版可以,但我找不到。可以为滚动视图重载 drawRect 以告诉它只在某个矩形内绘制。
      • 再次嗨,人们知道我是如何解决这个问题的,我探索了 UIScrollView 的子类化并重载 drawRect 以通过上下文绘图添加剪贴蒙版,但没有成功。 UIScrollView 认为它的边界是所有图像的整个长度,所以这可能就是原因。因此,由于我不需要剪裁区域完全透明,所以我最终做的是创建下面的图像,并通过 Interface Builder 中的 UIImageViews 将它们分层。
      【解决方案7】:

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

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

      【讨论】:

      • 感谢您的建议; contentInset 看起来确实很有希望,并且是我尝试的第一件事。不幸的是,它在所有内容周围添加了插图,而不是在每个页面周围(实际上它根本不能很好地与分页模式配合使用)。
      【解决方案8】:

      这只是一种预感,如果完全错了请见谅,但是否有可能将 contentSize 设置为比屏幕宽度略宽。

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

      【讨论】:

      • 感谢您的建议。问题是 contentSize 指定了所有页面的大小一起;没有办法控制单个页面的大小——它总是滚动视图的宽度。 (我们在这里讨论的是 pagingEnabled 模式!)我可以使滚动视图更宽以使页面更宽,但是滚动条会稍微偏离屏幕,这很难看。
      • 原来我已经在对您的回答的评论中描述了正确的解决方案。分页模式下没有滚动条,所以让滚动视图大于屏幕就是答案。我只是在另一个人提出这个建议后才意识到这一点,但正是你的回答激发了我首先思考正确的想法。所以继续张贴预感,他们确实有帮助! :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-23
      • 1970-01-01
      • 2012-03-01
      • 1970-01-01
      • 2013-07-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多