Как перевернуть UIView вокруг оси X, одновременно переключая подпредставления - PullRequest
9 голосов
/ 11 марта 2012

Этот вопрос задавался ранее, но немного по-другому, и я не смог заставить ни один из ответов работать так, как мне хотелось, поэтому я надеюсь, что кто-то с отличными навыками Core Animation сможет мне помочь.

У меня есть набор карт на столе.Когда пользователь проводит пальцем вверх или вниз, набор карточек перемещается вверх и вниз по столу.В любой момент времени на экране видны 4 карты, но только вторая карта показывает свое лицо.Когда пользователь проводит пальцем, вторая карта переворачивается на свое лицо, и следующая карта (в зависимости от направления движения) приземляется на свое место, показывая свое лицо.

Я настроил свой класс просмотра карт следующим образом:

@interface WLCard : UIView {
    UIView *_frontView;
    UIView *_backView;
    BOOL flipped;
}

И я попытался перевернуть карту, используя этот фрагмент кода:

- (void) flipCard {
    [self.flipTimer invalidate];
    if (flipped){
        return;
    }

    id animationsBlock = ^{
            self.backView.alpha = 1.0f;
            self.frontView.alpha = 0.0f;
            [self bringSubviewToFront:self.frontView];
            flipped = YES;

            CALayer *layer = self.layer;
            CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
            rotationAndPerspectiveTransform.m34 = 1.0 / 500;
            rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, M_PI, 1.0f, 0.0f, 0.0f);
            layer.transform = rotationAndPerspectiveTransform;
    };
    [UIView animateWithDuration:0.25
                          delay:0.0
                        options: UIViewAnimationCurveEaseInOut
                     animations:animationsBlock
                     completion:nil];

}

Этот код работает, но у него есть следующие проблемы, с которыми я не могу разобраться:

  1. Анимируется только половина карточки по оси X.
  2. После переворачивания лицевая сторона карточки переворачивается и зеркально отражается.
  3. Как только яперевернул карту, я не могу запустить анимацию снова.Другими словами, я могу запустить блок анимации столько раз, сколько захочу, но анимация будет выполняться только в первый раз.В следующий раз я пытаюсь анимировать, что приводит только к постепенному исчезновению между подпредставлениями.

Кроме того, имейте в виду, что мне нужно иметь возможность взаимодействовать с лицом карты.то есть на нем есть кнопки.

Если кто-нибудь сталкивался с этими проблемами, было бы здорово увидеть ваши решения.Еще лучше было бы добавить перспективное преобразование к анимации, чтобы придать ему дополнительный реализм.

Ответы [ 5 ]

12 голосов
/ 12 марта 2012

Это оказалось намного проще, чем я думал, и мне не пришлось использовать какие-либо библиотеки CoreAnimation для достижения эффекта.Спасибо @Aaron Hayman за подсказку.Я использовал transitionWithView:duration:options:animations:completion

Моя реализация в представлении контейнера:

    [UIView transitionWithView:self 
                      duration:0.2 
                       options:UIViewAnimationOptionTransitionFlipFromBottom
                    animations: ^{
                            [self.backView removeFromSuperview];
                            [self addSubview:self.frontView];
                    }
                    completion:NULL];

Хитрость была в опции UIViewAnimationOptionTransitionFlipFromBottom.Кстати, в документации Apple есть именно этот фрагмент кода.Вы также можете добавить другие анимации в блок, такие как изменение размера и перемещение.

3 голосов
/ 11 марта 2012

Хорошо, это не будет полным решением, но я укажу некоторые вещи, которые могут быть полезны.Я не гуру Core-Animation, но я сделал несколько вращений 3D в моей программе.

Во-первых, нет никакого «возврата» к виду.Поэтому, если вы повернете что-то на M_PI (180 градусов), вы будете смотреть на этот вид как бы сзади (именно поэтому он перевернут / зеркально отражен).

Я не уверен, что вы подразумеваете под:

Анимируется только половина карты по оси X.

Но этоэто может помочь рассмотреть вашу опорную точку (точку, в которой происходит вращение).Обычно это в центре, но часто вам нужно, чтобы было иначе.Обратите внимание, что точки привязки выражены в виде пропорции (в процентах / 100) ... поэтому значения равны 0 - 1,0f.Вам нужно установить его только один раз (если вам не нужно его менять).Вот как вы получаете доступ к узловой точке:

1012 *layer.anchorPoint = CGPointMake(0.5f, 0.5f) //This is center 1015 * Причина анимация только когда выполняется один раз, потому что преобразования являются абсолютными, не суммируется.Учтите, что вы всегда начинаете с преобразования идентификаторов, а затем модифицируете его, и это будет иметь смысл ... но, по сути, анимация не происходит, потому что во второй раз анимировать нечего (представление уже находится в состоянии, в котором вы находитесьс просьбой быть в).

Если вы анимируете от одного вида к другому (и вы не можете использовать [UIView transitionWithView:duration:options:animations:completion:];), вам придется использовать двухэтапную анимацию.На первом этапе анимации для «карты», которая переворачивается на обратную сторону, вы поворачиваете «исчезающий» вверх / вниз / куда угодно «M_PI_2» (в этот момент он «исчезнет»,или не видно, из-за его вращения).И на втором этапе вы поворачиваете заднюю сторону вида, чтобы исчезнуть в 0 (который должен быть преобразованием идентичности ... иначе нормальное состояние представления).Кроме того, вы должны сделать прямо противоположное для «карты», которая появляется (передняя сторона).Вы можете сделать это, внедрив еще один [UIView animateWithDuration:...] в блоке завершения первого.Я предупрежу вас, хотя, сделать это может быть немного сложнее.Тем более, что вы хотите, чтобы у видов была «задняя сторона», что в основном потребует анимации четырех видов (вид, который исчезает, вид, который должен появиться, задняя сторона, чтобы исчезнуть, и задняя сторона -от вида к печати).Наконец, в блоке завершения второй анимации вы можете выполнить некоторую очистку (сбросить представление, которое вращается и делает его альфа 0.0f и т. Д.).

Я знаю, что это сложно, так что вы можете захотетьПрочитайте учебник по Core-Animation.

1 голос
/ 12 марта 2012

@ У Аарона есть хорошая информация, которую вы должны прочитать.

Самое простое решение - использовать CATransformLayer , который позволит вам разместить другие CALayer .внутри и поддерживать их трехмерную иерархию.

Например, чтобы создать «Карту», ​​которая имеет переднюю и заднюю стороны, вы можете сделать что-то вроде этого:

CATransformLayer *cardContainer = [CATransformLayer layer];
cardContainer.frame = // some frame;

CALayer *cardFront  = [CALayer layer];
cardFront.frame     = cardContainer.bounds;
cardFront.zPosition = 2;   // Higher than the zPosition of the back of the card
cardFront.contents  = (id)[UIImage imageNamed:@"cardFront"].CGImage;
[cardContainer addSublayer:cardFront];

CALayer *cardBack  = [CALayer layer];
cardBack.frame     = cardContainer.bounds;
cardBack.zPosition = 1;
cardBack.contents  = (id)[UIImage imageNamed:@"cardBack"].CGImage; // You may need to mirror this image
[cardContainer addSublayer:cardBack];

Теперь вы можете применить своюпреобразуйте в cardContainer и получите карточку с переворачиванием.

0 голосов
/ 08 июня 2017

На основе Paul.s это обновляется для Swift 3 и переворачивает карту по диагонали:

func createLayers(){
  transformationLayer = CATransformLayer(layer: CALayer())
  transformationLayer.frame = CGRect(x: 15, y: 100, width: view.frame.width - 30, height: view.frame.width - 30)

  let black = CALayer()
  black.zPosition = 2
  black.frame = transformationLayer.bounds
  black.backgroundColor = UIColor.black.cgColor

  transformationLayer.addSublayer(black)

  let blue = CALayer()
  blue.frame = transformationLayer.bounds
  blue.zPosition = 1
  blue.backgroundColor = UIColor.blue.cgColor

  transformationLayer.addSublayer(blue)

  let tgr = UITapGestureRecognizer(target: self, action: #selector(recTap))
  view.addGestureRecognizer(tgr)
  view.layer.addSublayer(transformationLayer)
}

Анимация на все 360, но поскольку слои имеют разные z-позиции, разные "стороны" слоевшоу

func recTap(){
    let animation = CABasicAnimation(keyPath: "transform")
    animation.delegate = self
    animation.duration = 2.0
    animation.fillMode = kCAFillModeForwards
    animation.isRemovedOnCompletion = false
    animation.toValue = NSValue(caTransform3D: CATransform3DMakeRotation(CGFloat(Float.pi), 1, -1, 0))
    transformationLayer.add(animation, forKey: "arbitrarykey")
  }
0 голосов
/ 24 мая 2012

@Paul.s

Я следовал вашему подходу с контейнером карты, но когда я применяю анимацию вращения на контейнере карты, только одна половина первой карты вращается вокруг себя, и, наконец, появляется весь вид.сторона отсутствует в анимации

...