Как перевернуть 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];
}
этот код работает, но у него есть следующие проблемы с ним, которые я не могу понять:
- анимирована только половина карты по оси X.
- как только перевернуто, лицо карта перевернута и зеркально отражена.
- как только я перевернул карту, я не могу заставить анимацию работать снова. Другими словами, я могу запускать анимационный блок столько раз, сколько захочу, но только первый раз будет анимироваться. Последующие разы, когда я пытаюсь оживить, приводят к просто затуханию между подвидами.
кроме того, имейте в виду, что мне нужно иметь возможность взаимодействовать с лицом карты. т. е. на нем есть кнопки.
Если кто-нибудь имеет столкнулся с этими вопросами, было бы здорово увидеть ваши решения. Еще лучше было бы добавить перспективное преобразование в анимацию, чтобы дать ей дополнительный бит реализма.
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")
}