如何在UIScrollView缩放后重置?

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

当我放大和缩小UIScrollView ,我希望graphicsdynamicresize,就像从viewForZoomingInScrollView返回graphicsviewForZoomingInScrollView 。 但是,graphics会以新的缩放级别重新绘制自己,我想将变换比例重置为1×1,以便下次用户放大时,变换将从当前视图开始。 如果我将scrollViewDidEndZooming的转换重置为Identity,它将在模拟器中工作,但会在设备上引发EXC_BAD_ACCSES

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

所以首先:如何让graphics在缩放之后以1×1的标准比例重绘自己,以及如何在整个过程中进行平滑缩放?

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

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

我不能帮助你崩溃,除了告诉你检查,并确保你不是无意中自动释放一个视图或图层在你的代码中的某个地方。 我已经看到模拟器处理自动释放的时间不同于设备上的时间(通常涉及到线程时)。

但是,视图缩放是我遇到的UIScrollView一个问题。 在捏缩放事件, UIScrollView将采取您在viewForZoomingInScrollView:委托方法中指定的视图,并应用一个转换。 这种变换提供了视图的平滑缩放,而不必每帧重绘。 在缩放操作结束时,您的委托方法scrollViewDidEndZooming:withView:atScale:将被调用,并给您一个机会在新的比例因子下对视图进行更高质量的渲染。 一般来说,build议您将视图上的变换重置为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是视图的浮点实例variables。 然后我执行缩放委托方法如下:

 - (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的比例。 这似乎提供了正确的缩放行为,同时让您在完成缩放时绘制清晰的视图。

更新(7/23/2010):iPhone OS 3.2及以上版本已经改变了滚动视图在缩放方面的行为。 现在, UIScrollView将尊重您应用于内容视图的身份转换,并仅在-scrollViewDidEndZooming:withView:atScale:提供相对缩放因子-scrollViewDidEndZooming:withView:atScale: 因此,对于UIView子类,上述代码仅对运行早于3.2的iPhone OS版本的设备是必需的。

感谢所有以前的答案,这里是我的解决scheme。
实现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的出口
  • 之前设置maximumZoomScaleminimumZoomScale
  • 创buildpreviousScale实例variables并将其值设置为1.0f

完美地为我工作。

BR,Eugene。

我详细讨论了如何(以及为什么)UIScrollView缩放工作在github.com/andreyvit/ScrollingMadness/ 。

(该链接还包含了如何以编程方式缩放UIScrollView的说明,如何模拟Photo Library风格的分页+缩放+滚动,一个示例项目和ZoomScrollView类,封装了一些缩放魔术。

引用:

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

事实上,如果你看反汇编,它是UIView存储它自己的缩放级别(在_gestureInfo字段指向的UIGestureInfo对象内)。 它也有一些很好的未setZoomScale:animated:方法,如zoomScalesetZoomScale:animated: (请注意,它也有一些旋转相关的方法,也许我们很快就会获得旋转手势支持。)

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

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

你会需要:

  • 维持以前规模的伊娃; 我们称之为oldScale

  • viewForZoomingInScrollView:scrollViewDidEndZooming:确定可伸缩视图的一种方法。 在这里,我要申请一个标签(999)

  • 一个创build可伸缩视图的方法,标记它并将其插入到滚动视图(这里我将称之为addNewScalableViewAtScale: 。 请记住,它必须按比例做所有事情 – 调整视图及其子视图或绘图的大小,并调整滚动视图的contentSize以匹配。

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

那好吧。 当您创build滚动视图时,您将以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; 

请注意,由Brad提供的解决scheme有一个问题,如果你保持编程缩放(让我们说双击事件),让用户手动(捏出)缩小,一段时间之后,真正的规模和规模UIScrollView跟踪会变得太大,所以previousScale会在某些时候超出float的精度,最终导致不可预知的行为

我只是想重新加载到默认的缩放比例,因为当我缩放它时,滚动视图具有相同的缩放比例下一次,直到它被取消分配。

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

改变了游戏。