en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

10.19.2012

實作可以旋轉、拖曳與縮放的 UIView

  

UIView 的旋轉、拖曳與縮放是在 iOS 中很常被使用的技巧,下面我們以 UIImageView 為例,並且透過手指與螢幕的互動來完成這些效果,在互動的部份我們使用手勢辨識 UIGestureRecognizer 物件,你可以參考手勢辨識 UIGestureRecognizer 物件一文,了解 UIGestureRecognizer 的使用方法,並完成相關設定,最後透過 CGAffineTransform 技術來完成 UIImageView 的旋轉、拖曳與縮放動作。


停用 AutoLayout 功能
AutoLayout 這個功能是在 iOS 6.0 才開始出現,當 AutoLayout 啟用時你可以在 Storyboard 的 View Controller Scene 上看到一個藍色的 Constraint 物件,裡頭會記錄許多畫面物件排版的限制與對齊方式,這時,我們無法隨意地移動介面上的物件(在程式執行時使用 CGAffineTransform 也一樣),這時會出現無法以中心點做旋轉、以中心點做縮放,或是無法在 XY 座標空間移動等情形。

View Controller Scene 中的 Constraint 物件

停用 AutoLayout 功能的方法,在 Storyboard 中右手邊的 Show the File Inspector 檢視器分頁中,找到 Use AutoLayout 項目,取消勾選即可(預設是開啟)。

取消勾選 Show the File Inspector 中的 Use AutoLayout 項目


UIView 的旋轉、拖曳與縮放功能
假設我們的 UIGestureRecognizer 手勢辨識物件都已經設定完成,接下來就是針對手勢辨識成功之後所要採取的反應加以實作。

  • 製做暫存變數
在開始之前,我們要先對此類別宣告幾個全域變數當做暫存來使用,目的是為了在取得縮放或是旋轉等向量時,可以將之前的結果與目前所的到的結果做運算,最後才反應在 UIImageView 上。
@interface ViewController : UIViewController {

    //旋轉與縮放向量的暫存變數
    CGFloat scale, rotate;
}

ps:並不一定非得使用全域變數,如果程式設計的夠好,則可以避掉這一部分,但是將先後所得的兩個向量做運算是無法避免的。


  • 旋轉 
//旋轉手勢
- (IBAction)doRotate:(UIRotationGestureRecognizer *)sender {
    if([sender state] == UIGestureRecognizerStateBegan) {
        rotate = sender.rotation;
    }

    rotate = (sender.rotation - rotate);
    CGAffineTransform transform = CGAffineTransformRotate(sender.view.transform, rotate);
    [sender.view setTransform:transform];

    rotate = sender.rotation;
}


  • 拖曳
//拖曳手勢
- (IBAction)doMovement:(UIPanGestureRecognizer *)sender {
    [sender.view setCenter:[sender locationInView:self.view]];
}


  • 縮放
//縮放手勢
- (IBAction)doScale:(UIPinchGestureRecognizer *)sender {
    if([sender state] == UIGestureRecognizerStateBegan) {
        scale = sender.scale;
    }

    scale = 1 + (sender.scale - scale);
    CGAffineTransform transform = CGAffineTransformScale(sender.view.transform, scale, scale);
    [sender.view setTransform:transform];

    scale = [sender scale];
}

在上述程式碼中,旋轉與縮放的原理是一樣的,而在拖曳的部份則是使用更為直覺的 setCenter: 方法函式來做移動,

ps:在此並沒有針對拖曳時的位移差做補償,因為我們假定每次做拖曳的互動時,都是點擊在 UIImageView 的正中心。


其他應用
這邊提供兩個參數來幫助大家做運算,取得目前 UIImageView 旋轉的角度與縮放過後的比例,由於這個方法需要動用到 UIView 的 Layer,所以在使用以下方法前,記得先替專案加上 QuartzCore.framework 與其標頭檔,方法請參考 Xcode 4 新增 Framework 的方法一文。
//取得UIView本身的旋轉角度
CGFloat rotation = [[imageView.layer valueForKeyPath:@"transform.rotation"] floatValue];

//取得UIView本身的縮放比例
CGFloat size = [[imageView.layer valueForKeyPath:@"transform.scale"] floatValue];

ps:取得目前縮放的比例、旋轉角度或是位置,亦可從 imageView.transform 的 a, b, c, d, tx, ty 等屬性進行運算獲得,請參考官方網站的 CGAffineTransform Reference





沒有留言:

張貼留言