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