我有一个图形被绘制在一个UIScrollView
.它是一个UIView
使用自定义子类CATiledLayer
作为其层的大型.
当我放大和缩小时UIScrollView
,我希望图形动态调整大小,就像我从图表返回时一样viewForZoomingInScrollView
.但是,Graph在新的缩放级别重绘自己,我想将变换比例重置为1x1,以便下次用户缩放时,变换从当前视图开始.如果我将转换重置为Identity in scrollViewDidEndZooming
,它可以在模拟器中工作,但会EXC_BAD_ACCSES
在设备上抛出.
这甚至都没有在模拟器上完全解决问题,因为下次用户放大时,变换将自身重置为它所处的任何缩放级别,因此看起来,如果我缩放到2x,例如,它突然达到4倍.当我完成缩放时,它以正确的比例结束,但实际的缩放行为看起来很糟糕.
首先:如何让图形在缩放后以1x1的标准比例重绘自己,如何在整个过程中进行平滑缩放?
编辑:新发现错误似乎是" [CALayer retainCount]: message sent to deallocated instance
"
我自己永远不会释放任何图层.在此之前,我甚至没有删除任何观点或任何内容.在缩放和旋转时抛出此错误.如果我在旋转之前删除对象并在之后重新添加它,它不会抛出异常.这不是缩放的选项.
除了告诉您检查并确保您不会无意中自动释放代码中的某个视图或图层,我无法帮助您解决崩溃问题.我已经看到模拟器处理自动释放的时间与设备上的不同(最常见的是涉及线程时).
但是,视图缩放是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是视图的float实例变量.然后我实现缩放委托方法如下:
- (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及更高版本更改了滚动视图在缩放方面的行为.现在,a UIScrollView
将尊重您应用于内容视图的身份转换,并仅提供相对比例因子-scrollViewDidEndZooming:withView:atScale:
.因此,上述代码UIView
只适用于运行早于3.2的iPhone OS版本的设备.
感谢以前的所有答案,这是我的解决方案.
实现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的出口
之前设置maximumZoomScale和minimumZoomScale
创建previousScale实例变量并将其值设置为1.0f
完美适合我.
BR,Eugene.
我只是想将加载重置为默认缩放比例,因为当我缩放它时,scrollview下次具有相同的缩放比例,直到它被取消分配.
-(void) viewWillAppear:(BOOL)animated { [self.scrollView setZoomScale:1.0f]; }
改变了游戏.
我在github.com/andreyvit/ScrollingMadness/上详细讨论了UIScrollView缩放的工作原理(以及原因).
(该链接还包含有关如何以编程方式缩放UIScrollView,如何模拟照片库样式分页+缩放+滚动,示例项目和ZoomScrollView类的说明,该类封装了一些缩放魔法.)
引用:
UIScrollView没有"当前缩放级别"的概念,因为它包含的每个子视图可能有自己的当前缩放级别.请注意,UIScrollView中没有字段来保持当前缩放级别.但是我们知道有人存储缩放级别,因为如果你捏缩放子视图,然后将其变换重置为CGAffineTransformIdentity,然后再次捏,你会注意到子视图的先前缩放级别已经恢复.
实际上,如果你看一下反汇编,UIView就会存储它自己的缩放级别(在_gestureInfo字段指向的UIGestureInfo对象中).它还有一组很好的未记录的方法,比如zoomScale
和setZoomScale:animated:
.(请注意,它也有一堆旋转相关的方法,也许我们很快就会获得旋转手势支持.)
但是,如果我们创建一个仅用于缩放的新UIView并将我们的真实可缩放视图添加为其子视图,我们将始终以缩放级别1.0开始.我对程序化缩放的实现基于这个技巧.