在可可触摸中实现基于触摸的旋转

时间:2010-05-06 11:04:13

标签: iphone objective-c cocoa-touch uiview rotation

我想知道在我的iPhone应用程序中实现基于旋转的拖动动作的最佳方法是什么。

我有一个UIView,我希望围绕它的中心旋转,当用户的手指触摸视图并移动它时。可以把它想象成需要用手指调节的表盘。

基本问题归结为:

1)我应该记住调用touchesBegan时的初始角度和变换,然后每次调用touchesMoved时,都会根据手指的当前位置对视图应用新的变换,例如:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

  UITouch *touch = [touches anyObject];
  CGPoint currentPoint = [touch locationInView:self]; //current position of touch   

 if (([touch view] == self) 
    &&  [Utility getDistance:currentPoint toPoint:self.middle] <= ROTATE_RADIUS //middle is centre of view
    && [Utility getDistance:currentPoint toPoint:self.middle] >= MOVE_RADIUS) { //will be rotation gesture

  //remember state of view at beginning of touch
  CGPoint top = CGPointMake(self.middle.x, 0);
  self.initialTouch = currentPoint;
  self.initialAngle = angleBetweenLines(self.middle, top, self.middle, currentPoint);   
  self.initialTransform = self.transform;
 }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{

  UITouch *touch = [touches anyObject];
  CGPoint currentPoint = [touch locationInView:self]; //current position of touch

  if (([touch view] == self) 
  &&  [Utility getDistance:currentPoint toPoint:self.middle] <= ROTATE_RADIUS
  && [Utility getDistance:currentPoint toPoint:self.middle] >= MOVE_RADIUS) { //a rotation gesture

  //rotate tile
  float newAngle = angleBetweenLines(self.middle, CGPointMake(self.middle.x, 0), self.middle, currentPoint); //touch angle
  float angleDif = newAngle - self.initialAngle; //work out dif between angle at beginning of touch and now.
  CGAffineTransform newTransform = CGAffineTransformRotate(self.initialTransform, angleDif); //create new transform
  self.transform = newTransform;  //apply transform.
}

OR

2)我应该简单地记住最后已知的位置/角度,并根据现在和现在之间的角度差异来旋转视图,例如:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

      UITouch *touch = [touches anyObject];
      CGPoint currentPoint = [touch locationInView:self]; //current position of touch   

     if (([touch view] == self) 
        &&  [Utility getDistance:currentPoint toPoint:self.middle] <= ROTATE_RADIUS
        && [Utility getDistance:currentPoint toPoint:self.middle] >= MOVE_RADIUS) { //will be rotation gesture

      //remember state of view at beginning of touch
      CGPoint top = CGPointMake(self.middle.x, 0);
      self.lastTouch = currentPoint;
      self.lastAngle = angleBetweenLines(self.middle, top, self.middle, currentPoint);  
     }
    }

    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{

      UITouch *touch = [touches anyObject];
      CGPoint currentPoint = [touch locationInView:self]; //current position of touch

      if (([touch view] == self) 
      &&  [Utility getDistance:currentPoint toPoint:middle] <= ROTATE_RADIUS
      && [Utility getDistance:currentPoint toPoint:middle] >= MOVE_RADIUS) { //a rotation gesture

      //rotate tile
      float newAngle = angleBetweenLines(self.middle, CGPointMake(self.middle.x, 0), self.middle, currentPoint); //touch angle
      float angleDif = newAngle - self.lastAngle; //work out dif between angle at beginning of touch and now.
      CGAffineTransform newTransform = CGAffineTransformRotate(self.transform, angleDif); //create new transform

      self.transform = newTransform;  //apply transform.
      self.lastTouch = currentPoint;
      self.lastAngle = newAngle;
    }

第二个选项对我来说更有意义,但它没有给出非常令人满意的结果(锯齿状更新和非平滑旋转)。在性能方面哪种方式最好(如果有的话)?

干杯!

2 个答案:

答案 0 :(得分:3)

它实际上比你尝试的要简单得多。

您需要三个数据点:

  • 您的观点的起源。
  • 当前触摸的位置
  • 上一次触摸的位置

传递给您的Touch对象实际上包含最后一个触摸位置。所以你不需要跟踪它。

您所要做的就是计算两条线之间的角度:

  • Origin to Current Touch
  • Origin to Previous Touch

然后将其转换为弧度并在CGAffineTransformRotate()中使用它。在你的touchesMoved处理程序中完成所有操作。

这是一个计算你需要的功能:

static inline CGFloat angleBetweenLinesInRadians(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {

    CGFloat a = line1End.x - line1Start.x;
    CGFloat b = line1End.y - line1Start.y;
    CGFloat c = line2End.x - line2Start.x;
    CGFloat d = line2End.y - line2Start.y;

    CGFloat line1Slope = (line1End.y - line1Start.y) / (line1End.x - line1Start.x);
    CGFloat line2Slope = (line2End.y - line2Start.y) / (line2End.x - line2Start.x);

    CGFloat degs = acosf(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));


    return (line2Slope > line1Slope) ? degs : -degs;    
}

由Jeff LaMarche提供:

http://iphonedevelopment.blogspot.com/2009/12/better-two-finger-rotate-gesture.html

示例:

UITouch *touch = [touches anyObject];
CGPoint origin = [view center];
CGFloat angle = angleBetweenLinesInRadians(origin, [touch previousLocationInView:self.superview.superview], origin, [touch locationInView:self.superview.superview]);

答案 1 :(得分:0)

您是否考虑过使用UIRotationGestureRecognizer?似乎已经有了逻辑,并且可能会使事情更简单。