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 才能正常瀏覽本網站,謝謝!

4.20.2012

實作可拖曳設定圖片的物件

  

可拖曳設定圖片的物件,可以運用在快捷建設定中,將圖片拖曳至預先設定好的設定區域上(上圖淺灰色方塊部分),設定區域就會將拖曳的圖片設定其中,如果是將圖片拖曳到非設定區域上,被拖曳的圖片則會返回它的起始位置,關於更詳細的示範,可以參考文內的 YouTube 連結。


參考文章
包含一些在實作此示範時,可能會需要使用到文章。


物件的介面宣告
首先新增一個繼承於UIImageView 的 Objective-C class -- MLDragingView,並宣告以下介面。
@interface MLDragingView : UIImageView {
    CGPoint offsetLocation;
    UIImageView *dragView;
}
- (MLDragingView *)initWithCoder:(NSCoder *)c;
@end

其中參數 offsetLocation 是用來輔助物件在拖曳時的位移量計算,而 dragView 則是當被拖曳的物件,這樣做的原意是,我們並不想讓使用者真正去移動 MLDragingView 這個物件,而是當使用者點擊 MLDragingView 物件準備要拖曳時,另外產生一個 dragView 讓使用者去拖曳設定的動作。

在物件初始化的部份則是使用 initWithCoder,讓 MLDragingView 與 storyBoard 上的UIImageView 元件做連結。


物件的實作
初始化
在實作檔的部份,首先是 initWithCoder 初始化方法函式,在方法函式中除了初始化 dragView 之外,還將 dragView 的 tag 設定成 -1,並且將 MLDragingView 設定成可以與使用者做互動(這樣才能觸發與 UITouch 有關的內建函式)。

另外 tag 的設定是為了在取得畫面上所有的 UIImageView 時,分辨是否為設定區域還是非設定區域,這部份的實作稍後就會看到。
- (MLDragingView *)initWithCoder:(NSCoder *)c {
self = [super initWithCoder:c];

    if (self) {
        dragView = [[UIImageView alloc]init];
        [dragView setTag:-1];
        [self setUserInteractionEnabled:YES];
    }

    return self;
}

拖曳
在完成初始化的程式碼之後,接下來我們要來實作與觸碰相關的三個內建方法函式 touchesBegan、touchesMoved 和 touchesEnded。

在 touchesBegan 的部份,除了要記錄當前觸碰的位置以便之後做位移量的換算之外,還要設定 dragView 的圖案內容與位置,然後顯示於畫面上,注意顯示在畫面上的方法函式並非 [self addSubview:] 而是 [[self superview] addSubview:],因為我們是要將 dragView 添加在主畫面上,並非 MLDragingView 上。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    offsetLocation = [[touches anyObject] locationInView:self];

    [dragView setImage:self.image];
    [dragView setFrame:self.frame];
    [[self superview] addSubview:dragView];
}

在 touchesMoved 部分則是實作出 dragView 被拖曳的效果。
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    CGPoint movePoint = [[touches anyObject] locationInView:self];
    CGRect frame = self.frame;

    frame.origin.x += movePoint.x - offsetLocation.x;
    frame.origin.y += movePoint.y - offsetLocation.y;

    [dragView setFrame:frame];
}

在 touchesEnded 的部份,我們使用一個 BOOL 型態的參數來判斷是否有找到符合的設定區域部分,如果有找到符合的 UIImageView ,就將其的圖案內容設定成與 dragView 相同,並將 dragView 移出畫面,如果為否,就呼叫讓 dragView 返回原先位置的動畫。

在判斷是否為設定區域上我們並沒有多做設定,凡是只要符合 UIImageView 類別的元件並且它正與 dragView 有「重疊」,都可以被設定,由於 dragView 也算是 UIImageView 的一員因此特別利用 tag 將其隔開排除在外。
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    BOOL find = NO;

    for (UIImageView *view in [[self superview] subviews]) {
        if (view.tag != -1 && CGRectIntersectsRect(dragView.frame, view.frame)) {
            [view setImage:dragView.image];
            [view setBackgroundColor:[UIColor clearColor]];
            [dragView removeFromSuperview];
            find = YES;
        }
    }

    if (!find) {
        [self returnAnimation];
    }
}

動畫
最後是讓 dragView 返回原先位置的動畫部分,這是一個我們自行定義的方法函式,除了讓 dragView 返回原先位置外,當動畫結束後,同樣也將 dragView 重畫面上移除。
- (void)returnAnimation {
    [UIView beginAnimations:@"Return" context:nil];
    [UIView setAnimationDuration:0.5];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)];
    [dragView setFrame:self.frame];

    [UIView commitAnimations];
}

- (void)animationFinished:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
    [dragView removeFromSuperview];
}


MLDragingView 物件的使用方法
MLDragingView 在使用上非常容易,由於我們已經對其建立了 initWithCoder 初始化方法函式,因此只要在 Storyboard 上隨意拉幾個 UIImageView 元件,設定好內圖案內容,並且將它們的 Custom Class 改成 MLDragingView 即可。

在本範例中的 4 個 UIImageView 中,只有左手邊的 UIImageView 的 Custom Class 是屬於 MLDragingView,右邊的三個 UIImageView 的 Custom Class 則是維持原樣,因此他們只能「被設定」,而不能拖曳設定其他的 UIIMageView。


YouTube 示範






4 則留言:

  1. 匿名6/09/2012

    想再UIImageView 類別的元件並且它正與 dragView 有「重疊」的情況之下,進行跳頁的動作,但是都無法實作出來,不知道是哪一個環節出錯了。

    擷取 for迴圈內

    ------------------------------------------------
    testmc *b =[[testmc alloc] initWithNibName:nil bundle:nil];



    [UIView transitionFromView:self.superview
    toView:b.view
    duration:0.5
    options:UIViewAnimationOptionTransitionFlipFromLeft
    completion:^(BOOL finished){

    }];



    [dragView removeFromSuperview];
    find = YES;

    ------------------------------------------------
    這樣似乎可以換到下一頁,但是只要按button都會出現錯誤,真是orz

    回覆刪除
    回覆
    1. 您好:

      按下 button 會出錯,是什麼樣的錯誤?會不會是按鈕與事件沒連結好?

      也許你可以先不要試著「換頁」,在程式開始時直接使用你產生的新 UIView,看看能不能正常運作。

      刪除
  2. 請問一下
    如果我想動態生成圖片
    如何才能設定他的class為MLDragingView?
    謝謝~

    回覆刪除
    回覆
    1. Frank-wu 您好:

      你可以參考這一篇「Class 類別初始化的寫法(建構式)」將初始話的方法寫到裡面即可。
      http://furnacedigital.blogspot.tw/2011/02/blog-post_18.html

      文內所使用的方法為「使用 initWithCoder 初始化 Storyboard 上的元件」,它是初始化storyboard上固有的物件。
      http://furnacedigital.blogspot.tw/2012/04/initwithcoder-storyboard.html

      刪除