Реализация ротации на основе касания в касании какао - PullRequest
4 голосов
/ 06 мая 2010

Мне интересно, как лучше всего реализовать перетаскивание на основе поворота в моем приложении для 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 ]

3 голосов
/ 17 августа 2010

На самом деле все гораздо проще, чем вы пробовали.

Вам нужно три точки данных:

  • Происхождение вашего взгляда.
  • Местоположение текущего касания
  • Местоположение предыдущего касания

Переданный вам объект Touch на самом деле содержит местоположение последнего касания. Так что вам не нужно следить за этим.

Все, что вам нужно сделать, это вычислить угол между двумя линиями:

  • Происхождение до текущего прикосновения
  • Происхождение к предыдущему касанию

Затем преобразуйте это в радианы и используйте это в вашем CGAffineTransformRotate (). Сделай все это в своих прикосновениях Перемещенный обработчик.

Вот функция для расчета того, что вам нужно:

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;    
}

Предоставлено Джеффом Ламаршем по адресу:

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

* +1032 * Пример: * 1 033 *
UITouch *touch = [touches anyObject];
CGPoint origin = [view center];
CGFloat angle = angleBetweenLinesInRadians(origin, [touch previousLocationInView:self.superview.superview], origin, [touch locationInView:self.superview.superview]);
0 голосов
/ 06 июня 2010

Рассматривали ли вы использование UIRotationGestureRecognizer? Похоже, в этом уже заложена логика, и это может упростить ситуацию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...