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

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

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


person Michael Gaylord    schedule 11.03.2012    source источник


Ответы (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];

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

person Michael Gaylord    schedule 12.03.2012

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

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

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

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

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

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

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

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

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

person Aaron Hayman    schedule 11.03.2012
comment
Спасибо, Аарон. Метод transitionWithView дал мне ключ к решению. При этом вы дали мне лучшее понимание того, как работает 3D-анимация. Пришло время инвестировать в хорошую книгу Core Animation. - person Michael Gaylord; 12.03.2012

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

Самое простое решение — использовать 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 и получить переворачивающуюся карту.

person Paul.s    schedule 11.03.2012
comment
Пока этот метод будет работать. Мне нужно иметь UIViews с интерактивными элементами на картах, так как пользователю нужно взаимодействовать с лицами карт. Использование CALayer позволяет мне работать только с изображениями и т. д. - person Michael Gaylord; 12.03.2012
comment
Поместите эту иерархию в содержащую UIView структуру, которая будет иметь дело с взаимодействиями... - person Paul.s; 12.03.2012

@Paul.s

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

person Ilker Baltaci    schedule 23.05.2012

На основе 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")
  }
person aBikis    schedule 08.06.2017