【问题标题】:How do I reset after a UIScrollView zoom?UIScrollView 缩放后如何重置?
【发布时间】:2010-10-01 16:31:01
【问题描述】:

我在UIScrollView 中绘制了一个图表。它是一个大的UIView,使用CATiledLayer 的自定义子类作为其层。

当我放大和缩小 UIScrollView 时,我希望图形像从 viewForZoomingInScrollView 返回图形时那样动态调整大小。但是,图表会在新的缩放级别重新绘制自身,我想将变换比例重置为 1x1,以便下次用户缩放时,变换从当前视图开始。如果我在 scrollViewDidEndZooming 中将转换重置为 Identity,它会在模拟器中运行,但会在设备上抛出 EXC_BAD_ACCSES

这甚至不能完全解决模拟器上的问题,因为下次用户缩放时,变换会自行重置为它所处的缩放级别,所以看起来,如果我被缩放到 2x,例如,它突然变为 4 倍。当我完成缩放时,它以正确的比例结束,但实际的缩放行为看起来很糟糕。

首先:如何让图形在缩放后以 1x1 的标准比例重绘自身,以及如何在整个过程中平滑缩放?

编辑:新发现 错误似乎是“[CALayer retainCount]: message sent to deallocated instance

我自己从不释放任何层。以前,我什至没有删除任何视图或任何东西。这个错误是在缩放和旋转时抛出的。如果我在旋转之前删除对象并在之后重新添加它,它不会引发异常。这不是缩放选项。

【问题讨论】:

    标签: iphone ios cocoa-touch uiscrollview zooming


    【解决方案1】:

    除了告诉您检查并确保您不会无意中自动释放代码中某处的视图或图层之外,我无法帮助您解决崩溃问题。我已经看到模拟器处理自动释放时间的方式与设备上不同(通常是在涉及线程时)。

    不过,我遇到过UIScrollView 的视图缩放问题。在捏缩放事件期间,UIScrollView 将采用您在viewForZoomingInScrollView: 委托方法中指定的视图并对其应用转换。这种变换提供了视图的平滑缩放,而不必在每一帧都重新绘制它。在缩放操作结束时,您的委托方法scrollViewDidEndZooming:withView:atScale: 将被调用,并让您有机会以新的比例因子对视图进行更高质量的渲染。通常,建议您将视图上的转换重置为CGAffineTransformIdentity,然后让您的视图以新的大小比例手动重绘。

    但是,这会导致一个问题,因为UIScrollView 似乎没有监视内容视图转换,因此在下一次缩放操作中,它将内容视图的转换设置为整体比例因子。由于您已在最后一个比例因子处手动重绘视图,因此它会复合缩放,这就是您所看到的。

    作为一种解决方法,我为内容视图使用 UIView 子类,并定义了以下方法:

    - (void)setTransformWithoutScaling:(CGAffineTransform)newTransform;
    {
        [super setTransform:newTransform];
    }
    
    - (void)setTransform:(CGAffineTransform)newValue;
    {
        [super setTransform:CGAffineTransformScale(newValue, 1.0f / previousScale, 1.0f / previousScale)];
    }
    

    previousScale 是视图的浮点实例变量。然后我实现缩放委托方法如下:

    - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale;
    {
        [contentView setTransformWithoutScaling:CGAffineTransformIdentity];
    // Code to manually redraw view at new scale here
        contentView.previousScale = scale;
        scrollView.contentSize = contentView.frame.size;
    }
    

    通过这样做,发送到内容视图的变换会根据上次重绘视图的比例进行调整。缩放完成后,通过绕过正常setTransform: 方法中的调整,将变换重置为1.0 的比例。这似乎提供了正确的缩放行为,同时让您在完成缩放时绘制清晰的视图。

    更新(2010 年 7 月 23 日):iPhone OS 3.2 及更高版本更改了滚动视图在缩放方面的行为。现在,UIScrollView 将尊重您应用于内容视图的身份转换,并且仅在-scrollViewDidEndZooming:withView:atScale: 中提供相对比例因子。因此,UIView 子类的上述代码仅适用于运行 iPhone OS 版本低于 3.2 的设备。

    【讨论】:

    • 为什么内容视图总是消失?
    • 您没有提供太多信息,但如果您没有手动隐藏或从层次结构中删除内容视图,我的猜测是它变得大于 GPU 支持的纹理大小。如果视图变得比 2048 像素更宽或更高,它就不能再在 iPhone 和 iPhone 3G 上呈现(我不能代表 3G S)。
    • 我重绘成功,分片清晰。但我注意到它减慢了缩放速度。你知道为什么吗?谢谢。
    • 放大到哪一部分?捏拉放大和缩小应该非常平滑,因为视图只是在缩放操作的那部分进行了变换。如果应用程序在缩放结束时重绘视图的速度很慢,您可能需要使用 Shark 或 Instruments 查看您的绘制操作,以了解瓶颈在哪里。
    • 我在 UIView 中有一个 UIImageView 网格。可能是我的手机,我用的是第一代手机。
    【解决方案2】:

    感谢所有以前的答案,这是我的解决方案。
    实现 UIScrollViewDelegate 方法:

    - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
    {
        return tmv;
    }
    
    - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
    {
        CGPoint contentOffset = [tmvScrollView contentOffset];   
        CGSize  contentSize   = [tmvScrollView contentSize];
         CGSize  containerSize = [tmv frame].size;
    
        tmvScrollView.maximumZoomScale = tmvScrollView.maximumZoomScale / scale;
        tmvScrollView.minimumZoomScale = tmvScrollView.minimumZoomScale / scale;
    
        previousScale *= scale;
    
        [tmvScrollView setZoomScale:1.0f];
    
        [tmvScrollView setContentOffset:contentOffset];
        [tmvScrollView setContentSize:contentSize];
        [tmv setFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)];  
    
        [tmv reloadData];
    }
    
    • tmv 是我的 UIView 的子类
    • tmvScrollView — UIScrollView 的出口
    • 之前设置ma​​ximumZoomScaleminimumZoomScale
    • 创建 previousScale 实例变量并将其值设置为 1.0f

    非常适合我。

    BR,尤金。

    【讨论】:

      【解决方案3】:

      我只是想在加载时将其重置为默认缩放比例,因为当我缩放它时,滚动视图在下次被释放之前具有相同的缩放比例。

      -(void) viewWillAppear:(BOOL)animated {
            [self.scrollView setZoomScale:1.0f];
      }
      

      改变了游戏。

      【讨论】:

        【解决方案4】:

        我在github.com/andreyvit/ScrollingMadness/ 详细讨论了 UIScrollView 缩放的工作原理(以及原因)。

        (该链接还包含对如何以编程方式缩放 UIScrollView、如何模拟照片库样式的分页+缩放+滚动的描述、示例项目和封装了一些缩放魔术的 ZoomScrollView 类。)

        引用:

        UIScrollView 没有“当前缩放级别”的概念,因为它包含的每个子视图都可能有自己的当前缩放级别。请注意,UIScrollView 中没有字段来保持当前缩放级别。但是我们知道有人存储了该缩放级别,因为如果您捏缩放子视图,然后将其变换重置为 CGAffineTransformIdentity,然后再次捏,您会注意到子视图之前的缩放级别已恢复。

        确实,如果您查看反汇编,它是存储自己的缩放级别的 UIView(在 _gestureInfo 字段指向的 UIGestureInfo 对象内)。它还有一组很好的未记录方法,例如 zoomScalesetZoomScale:animated:。 (请注意,它还有一堆与旋转相关的方法,也许我们很快就会支持旋转手势。)

        但是,如果我们创建一个仅用于缩放的新 UIView 并将我们真正的可缩放视图添加为其子视图,我们将始终从缩放级别 1.0 开始。我的程序化缩放实现就是基于这个技巧。

        【讨论】:

        • zoomScale 是 UIScrollView 的一部分,而不是 UIView。
        • 我想你现在已经移动了你的疯狂滚动内容?你能更新链接吗(我还没找到新家)。
        • @Benjohn 嘿,多年前它已经过时了。但是现在您可以在 UIScrollView 上重置 zoomScale,因此不再需要这种技巧(从 iOS 3 或 4 开始)。
        【解决方案5】:

        一种相当可靠的方法,适用于 iOS 3.2 和 4.0 及更高版本,如下所示。您必须准备好以任何请求的比例提供您的可缩放视图(滚动视图的主要子视图)。然后在scrollViewDidEndZooming: 中,您将删除此视图的模糊比例转换版本,并将其替换为绘制到新比例的新视图。

        你需要:

        • 一个用于保持先前比例的 ivar;我们称之为 oldScale

        • 一种标识可伸缩视图的方法,适用于viewForZoomingInScrollView:scrollViewDidEndZooming:;在这里,我要应用一个标签(999)

        • 一种创建可伸缩视图、对其进行标记并将其插入到滚动视图中的方法(这里我称之为addNewScalableViewAtScale:)。请记住,它必须根据比例做所有事情 - 它调整视图及其子视图或绘图的大小,并调整滚动视图的 contentSize 大小以匹配。

        • minimumZoomScale 和 maximumZoomScale 的常量。这些是必需的,因为当我们用详细的大版本替换缩放视图时,系统会认为当前比例为 1,因此我们必须欺骗它允许用户缩小视图。

        那么很好。创建滚动视图时,以 1.0 的比例插入可缩放视图。这可能在您的viewDidLoad 中,例如(sv 是滚动视图):

        [self addNewScalableViewAtScale: 1.0];
        sv.minimumZoomScale = MIN;
        sv.maximumZoomScale = MAX;
        self->oldScale = 1.0;
        sv.delegate = self;
        

        然后在你的scrollViewDidEndZooming: 中你做同样的事情,但补偿规模的变化:

        UIView* v = [sv viewWithTag:999];
        [v removeFromSuperview];
        CGFloat newscale = scale * self->oldScale;
        self->oldScale = newscale;
        [self addNewScalableViewAtScale:newscale];
        sv.minimumZoomScale = MIN / newscale;
        sv.maximumZoomScale = MAX / newscale;
        

        【讨论】:

          【解决方案6】:

          请注意,Brad 提供的解决方案有一个问题:如果您继续以编程方式放大(比如说双击事件)并让用户手动(缩小)缩小,一段时间后真实比例之间的差异并且 UIScrollView 正在跟踪的规模会变得太大,所以 previousScale 在某些时候会超出浮点数的精度,最终会导致不可预知的行为

          【讨论】:

            【解决方案7】:

            Swift 4.*Xcode 9.3

            将 scrollView 缩放比例设置为 0 会将您的 scrollView 重置为其初始状态。

            self.scrollView.setZoomScale(0.0, animated: true)
            

            【讨论】:

            • 你的意思是1.0吗? self.scrollViewForZoom.setZoomScale(1.0, animated: true)
            • 没有。如果你想缩放使用 self.scrollView.zoom(to: rectTozoom, animated: true) 如果你想缩小然后使用 self.scrollView.setZoomScale(0.0, animated: true)。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-20
            相关资源
            最近更新 更多