ZoomingViewController是一个类,你可以将这个类依附于任何一个存在的视图,之后仅需单击一下就可以将这个视图放大至全屏,或者旋转设备来使全屏视图旋转以及单击使其返回初始视图状态。
介绍
在这个项目中使用ZoomingViewController类来处理放缩视图,如下所示:
在原始视图和全屏视图两者间动态切换是很流畅的而且你可以在全屏下通过旋转设备来使视图转动。
你可以通过将ZoomingViewController类依附于任何一个视图来添加这个变换特性。
你可以在这里下载完整的工程文件:TapZoomRotate.zip
ZoomingViewController类有三个主要的特性:
- 可以响应触击行为
- 流畅地在不同的视图之间切换
- 允许在全屏状态下视图的旋转。
响应触击行为
通常情况下,你不必自己处理触击事件,像buttons或者tableviewcell的控制都有自带的触击检测机制。
在iOS3.2之前版本,检测触击事件意味着要在视图中实现touchesBegan:withEvent:和touchesEnded:withEvent:。这需要很高的技巧因为你必须根据计算来掌握触击的时间和位置以判断是否这个触击是有效的。另外,它还不能很好地支持ZoomingViewController类,因为这个类只是依附与这个视图而不是成为这个视图的一部分。
幸运的是,在IOS3.2中引入的手势识别器使得这个问题变得简单:
- singleTapGestureRecognizer =
- [[UITapGestureRecognizer alloc]
- initWithTarget:self action:@selector(toggleZoom:)];
- singleTapGestureRecognizer.numberOfTapsRequired = 1;
- [self.view addGestureRecognizer:singleTapGestureRecognizer];
流畅地在不同的视图之间切换
首先,我们需要记录初始视图以及它的位置。我们通过在视图位置插入一个代理视图来实现记录。以这种方式,当窗口旋转时,该代理视图也可以跟踪视图的位置。
- proxyView = [[UIView alloc] initWithFrame:self.view.frame];
- proxyView.hidden = YES;
- proxyView.autoresizingMask = self.view.autoresizingMask;
- [self.view.superview addSubview:proxyView];
之后,我们需要根据当前窗口坐标计算视图当前的位置。
- CGRect frame = [self.view.window convertRect:self.view.frame
- fromView:proxyView.superview];
- [self.view.window addSubview:self.view];
- self.view.frame = frame;
然后我们把视图由当前位置放大至全屏显示。
- [UIView
- animateWithDuration:0.2
- animations:^{
- self.view.frame = self.view.window.bounds;
- }];
- [[UIApplication sharedApplication]
- setStatusBarHidden:YES
- withAnimation:UIStatusBarAnimationFade];
允许在全屏状态下视图的旋转
ZoomingViewController类并不是UIViewController的子类。尽管它控制视图放大和缩小,但是它不需要借助UIViewController的任何行为。
即使你使这个类成为UIViewController的一个子类,它仍然不允许你使用UIViewController类中的自动处理旋转的行为。原因是shouldAutorotateToInterfaceOrientation:只能被窗口的第一个UIViewController来调用。由于fullscreen视图总是这个窗口的第二个视图,因此UIViewController的自动处理旋转行为不能满足我们转动的需要。
- 为确定的转动判断正确的全屏边界
- 在转动之后利用CGAffineTransform来将当前边界转换为新的边界。
- 监听UIDeviceOrientationDidChangeNotification,当变化发生时申请新值。
- 根据转动来获取全屏的边界也是相当简单的。我们需要计算面朝上和面朝下转动的不同——我通过计算状态栏转动来得到(我不会总是使用状态栏以防由于某种原因它与某个设备不同步)
- - (CGRect)rotatedWindowBounds
- {
- UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
- if (orientation == UIDeviceOrientationFaceUp ||
- orientation == UIDeviceOrientationFaceDown)
- {
- orientation = [UIApplication sharedApplication].statusBarOrientation;
- }
- if (orientation == UIDeviceOrientationLandscapeLeft ||
- orientation == UIDeviceOrientationLandscapeRight)
- {
- CGRect windowBounds = self.view.window.bounds;
- return CGRectMake(0, 0, windowBounds.size.height, windowBounds.size.width);
- }
- return self.view.window.bounds;
- }
在UIDeviceOrientationDidChangeNotification之后,我们申请新的边界值。
不幸的是,申请不同的边界会影响视图中心的坐标。由于最后一步需要申请一个转动变换而且转动总是围绕视图中心进行,所以我们需要变换视图以使得中心保持不变。
- - (CGAffineTransform)orientationTransformFromSourceBounds:(CGRect)sourceBounds
- {
- UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
- if (orientation == UIDeviceOrientationFaceUp ||
- orientation == UIDeviceOrientationFaceDown)
- {
- orientation = [UIApplication sharedApplication].statusBarOrientation;
- }
- if (orientation == UIDeviceOrientationPortraitUpsideDown)
- {
- return CGAffineTransformMakeRotation(M_PI);
- }
- else if (orientation == UIDeviceOrientationLandscapeLeft)
- {
- CGRect windowBounds = self.view.window.bounds;
- CGAffineTransform result = CGAffineTransformMakeRotation(0.5 * M_PI);
- result = CGAffineTransformTranslate(result,
- 0.5 * (windowBounds.size.height - sourceBounds.size.width),
- 0.5 * (windowBounds.size.height - sourceBounds.size.width));
- return result;
- }
- else if (orientation == UIDeviceOrientationLandscapeRight)
- {
- CGRect windowBounds = self.view.window.bounds;
- CGAffineTransform result = CGAffineTransformMakeRotation(-0.5 * M_PI);
- result = CGAffineTransformTranslate(result,
- 0.5 * (windowBounds.size.width - sourceBounds.size.height),
- 0.5 * (windowBounds.size.width - sourceBounds.size.height));
- return result;
- }
- return CGAffineTransformIdentity;
- }
最后,我们把这些值交给UIDeviceOrientationDidChangeNotification。监听这个报告是简单的,在全屏显示下我们只需要作为观察者就可以了。
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(deviceRotated:)
- name:UIDeviceOrientationDidChangeNotification
- object:[UIDevice currentDevice]];
唯一需要注意的一点就是当一个视图转动的时候,它的角后面的视图可能会暴露出来。为了避免这个潜在的不明显的情况,ZoomingViewController类使用一个空视图插入到转动视图的后面,当转动完成,这个空视图会消失。
下面deviceRotated:的实现包含以下的代码,包括创建并插入空视图,响应UIDeviceOrientationDidChangeNotification来实施转动。
- CGRect windowBounds = self.view.window.bounds;
- UIView *blankingView =
- [[[UIView alloc] initWithFrame:
- CGRectMake(-0.5 * (windowBounds.size.height - windowBounds.size.width),
- 0, windowBounds.size.height, windowBounds.size.height)] autorelease];
- blankingView.backgroundColor = [UIColor blackColor];
- [self.view.superview insertSubview:blankingView belowSubview:self.view];
- [UIView animateWithDuration:0.25 animations:^{
- self.view.bounds = [self rotatedWindowBounds];
- self.view.transform = [self orientationTransformFromSourceBounds:self.view.bounds];
- } completion:^(BOOL complete){
- [blankingView removeFromSuperview];
- }];
结论:
使用ZoomingViewController是很简单的:创建它,设置它的视图,之后视图可以响应触击事件,放大为全屏,在全屏下旋转。你可以将它依附给任何一个视图当你需要全屏显示时。
原文链接:http://cocoawithlove.com/2010/09/zoomingviewcontroller-to-animate-uiview.html