UIColorの混合

cocoaの場合、描画関係のAPIが充実しているので自分でグラデーションを描画する事は少ないと思われます。
が、まれーにグラデーションの中間色、色と色とを特定の割合で混合した色を求めたい状況があるわけです。

そんな時はこれ
weightが0の時はcolorFrom、100の時はcolorToの色となるように色成分を混合して
返してくれます。


// weight: 0~100
-(UIColor*)makeBlendColorFrom:(UIColor*)colorFrom colorTo:(UIColor*)colorTo weight:(float)weight
{
    float r1,g1,b1,r2,g2,b2,alpha;

    //RGB値を求める
    [colorFrom getRed:&r1 green:&g1 blue:&b1 alpha:&alpha];
    [colorTo getRed:&r2 green:&g2 blue:&b2 alpha:&alpha];

    //色成分を混合
    float red = (r2*weight + r1*(100.0 - weight))/100.0;
    float green = (g2*weight + g1*(100.0 - weight))/100.0;
    float blue = (b2*weight + b1*(100.0 - weight))/100.0;

    return [UIColor colorWithRed:red green:green blue:blue alpha:1];
}

UIViewの透過(見た目とイベント)

表示上の透過
方法1 alphaを0にする

view.alpha = 0;

これで透明になりますが、これではviewの上にのせた他のSubviewたちも表示されません。

方法2 背景色をクリアにする

view.backgroundcolor = [UIColor clearColor];

これで背景のみが透過されます。

イベントの透過
方法1 isUserInteractionEnabledを設定する

view. isUserInteractionEnabled = NO;

これでview に対してのイベントが透過されますが、subviewについても反応しなくなります。

方法2 hitTestをオーバーライド

こんな感じ


- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView* view= [super hitTest:point withEvent:event];
    if(view==self){
         return nil;
    }
    return view;
}

hitTestでnilを返すと後ろのviewへイベントが透過されます。
これを使うと、状況によってイベントの発生状況をコントロールする事が可能となります。

3本指のピンチジェスチャーを作る 3

touchesMovedの実装

次にtouchesMovedを実装していきます。

ここでやることは単純でタッチポイント間の距離を求めて、touchsBegan時点で保存した距離との比率を
プロパティscaleに保存すればOK

ただし、念のため3点以外でタッチされた時はジェスチャーを失敗させます。
また、stateがUIGestureRecognizerStatePossibleの時はUIGestureRecognizerStateBeganに書き換えておきます。


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];
    
    int numberOfTouches =  event.allTouches.count;
    if (numberOfTouches != 3)
    {
        if (self.state == UIGestureRecognizerStatePossible)
        {
            self.state = UIGestureRecognizerStateFailed;
        } else {
            self.state = UIGestureRecognizerStateCancelled;
        }
        return;
    }
    
    UITouch *first = [[event.allTouchesallObjects] objectAtIndex:0];
    UITouch *second = [[event.allTouchesallObjects] objectAtIndex:1];
    UITouch *third = [[event.allTouchesallObjects] objectAtIndex:2];
    
    float distance = [self calcDistance:[first locationInView:self.view]
                                     p2:[second locationInView:self.view]
                                     p3:[third locationInView:self.view]];
    
    self.scale = distance / _distance_initial;
    
    if (self.state == UIGestureRecognizerStatePossible)
    {
        self.state = UIGestureRecognizerStateBegan;
    } 
    
    
}

後処理の実装

あとは終了処理とキャンセルの処理を実装すれば完成です

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    
    switch (self.state) {
        caseUIGestureRecognizerStatePossible: 
        caseUIGestureRecognizerStateFailed:    
            self.state = UIGestureRecognizerStateFailed;
            break;
        caseUIGestureRecognizerStateBegan:
        caseUIGestureRecognizerStateChanged:
            // 正常に終了
            self.state = UIGestureRecognizerStateEnded;
            break;
        default:
            // 上記以外はキャンセル扱い
            self.state = UIGestureRecognizerStateCancelled;
            break;
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [supertouchesCancelled:touches withEvent:event];
    self.state = UIGestureRecognizerStateFailed;
    self.scale = 1;
}


使い方

使い方は通常のUIPinchGestureRecognizerと大体同じ

ジェスチャーをUIViewに登録して

ThreeFingersPinchGestureRecognizer *pinch = [[[ThreeFingersPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchAction:)] autorelease];
[self.view addGestureRecognizer:pinch];

ハンドリング

- (void)pinchAction : (ThreeFingersPinchGestureRecognizer *)sender 
{
  //sender.scale を使って拡大縮小などを行う
}

ファイル

3本指のピンチジェスチャーを作る 2

3点間の距離の求め方

3点間の距離の合計を求めるメソッドを作りましょう。
中学の時にならったアレです。

2点A(a,b),B(c,d)間の距離ABは
AB = √((c-a)の二乗 + (d-b)の二乗)

ってやつ
これを使って下記のようなメソッドを作成します。


//3点間の距離の合計を求める
- (float)calcDistance:(CGPoint)p1 p2:(CGPoint)p2 p3:(CGPoint)p3
{
    float distance = 0;

    float x;
    float y;

    x = fabs( p1.x - p2.x);
    y = fabs( p1.y - p2.y);
    distance += sqrt((x * x) + (y * y));

    x = fabs( p2.x - p3.x);
    y = fabs( p2.y - p3.y);
    distance += sqrt((x * x) + (y * y));

    x = fabs( p3.x - p1.x);
    y = fabs( p3.y - p1.y);
    distance += sqrt((x * x) + (y * y));

    return distance;
}

touchesBeganの実装

touchesBeganではジェスチャーの起動を判定する必要がありますが、
今回はタッチポイントが3点あるかどうかで判定します。

3点以外の時はstateにUIGestureRecognizerStateFailedを設定して、
ジェスチャーが失敗した事を通知します。


    //3本指の判定
    int numberOfTouches = event.allTouches.count;
    if (numberOfTouches != 3)

    {
        self.state = UIGestureRecognizerStateFailed;
        return;
    }

次にジェスチャー開始時点でのタップポイント間の距離を求めて保存します


    UITouch *first = [[event.allTouchesallObjects] objectAtIndex:0];
    UITouch *second = [[event.allTouchesallObjects] objectAtIndex:1];
    UITouch *third = [[event.allTouchesallObjects] objectAtIndex:2];

    _distance_initial = [self calcDistance:[first locationInView:self.view]
                        p2:[second locationInView:self.view]
                         p3:[third locationInView:self.view]];

    self.scale = 1;

※_distance_initialはインスタンス変数
scaleはプロパティです (例えば @property (nonatomic,assign) float scale; )

続きは次回