Как перевернуть UIView вокруг оси x при одновременном переключении подвидов

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

у меня есть набор карт на столе. По мере того, как пользователь проводит вверх или вниз набор карт двигаться вверх и вниз по столу. В любой момент времени на экране отображаются 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 ответов


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

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


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

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

Я не уверен, что вы имеете в виду:

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

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

layer.anchorPoint = CGPointMake(0.5f, 0.5f) //This is center

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

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

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


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

самое простое решение-это использовать CATransformLayer это позволит вам разместить другие CALayerвнутри и поддерживать свою 3D-иерархию.

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

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 и листать карту.


@Paul.s

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


на основе 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, но поскольку слои имеют разные zPositions, разные "стороны" слоев будут показывать

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")
  }