Внедрение пользовательской анимации для представления модального представления из указанного вида на iPad
На iPad у нас гораздо больше возможностей для работы, поэтому представление полноэкранных модальных представлений не является идеальным.
Я знаю, как представить модальные представления в новой форме. В этом вопросе можно найти близкий подход: iPad iTunes Animation
Проблема в том, что вы не можете выбрать, откуда будет происходить анимация, поэтому он просто по умолчанию и появляется из центра, я хочу настроить его так, чтобы он отображался в определенном месте.
Лучший пример, который я могу найти для этой анимации, можно увидеть в первые несколько секунд это видео
Если кто-то может указать мне в правильном направлении, используя код, учебники или документацию, я бы очень признателен!
Update:
После некоторого исследования я обнаружил, что это можно сделать с использованием слоев и Core Animation для первой части; а затем анимировать его formSheet модальный вид, но я до сих пор не совсем понимаю, как его достичь, надеюсь, вы, ребята, можете помочь!
Ответы
Ответ 1
Что я сделал, так это создание новой категории для UIViewController следующим образом
UIViewController + ShowModalFromView.h
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
@interface UIViewController (ShowModalFromView)
- (void)presentModalViewController:(UIViewController *)modalViewController fromView:(UIView *)view;
@end
UIViewController + ShowModalFromView.m
#import "UIViewController+ShowModalFromView.h"
@implementation UIViewController (ShowModalFromView)
- (void)presentModalViewController:(UIViewController *)modalViewController fromView:(UIView *)view
{
modalViewController.modalPresentationStyle = UIModalPresentationFormSheet;
// Add the modal viewController but don't animate it. We will handle the animation manually
[self presentModalViewController:modalViewController animated:NO];
// Remove the shadow. It causes weird artifacts while animating the view.
CGColorRef originalShadowColor = modalViewController.view.superview.layer.shadowColor;
modalViewController.view.superview.layer.shadowColor = [[UIColor clearColor] CGColor];
// Save the original size of the viewController view
CGRect originalFrame = modalViewController.view.superview.frame;
// Set the frame to the one of the view we want to animate from
modalViewController.view.superview.frame = view.frame;
// Begin animation
[UIView animateWithDuration:1.0f
animations:^{
// Set the original frame back
modalViewController.view.superview.frame = originalFrame;
}
completion:^(BOOL finished) {
// Set the original shadow color back after the animation has finished
modalViewController.view.superview.layer.shadowColor = originalShadowColor;
}];
}
@end
Это довольно прямолинейно. Пожалуйста, дайте мне знать, если это поможет вам.
UPDATE
Я обновил ответ на использование блоков анимации вместо пары [UIView beginAnimations:nil context:nil];
/[UIView commitAnimations]
.
Ответ 2
Похоже, вы по существу после перевода (перемещения) a CALayer
при масштабировании и одновременном вращении вокруг оси y. Попробуйте следующее:
NSValue *initialTransformValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
CATransform3D translation = CATransform3DMakeTranslation(finalPoint.x, finalPoint.y, 0.0);
CATransform3D scalingAndTranslation = CATransform3DScale(translation, kMyScalingFactor, kMyScalingFactor, 1.0);
CATransform3D finalTransform = CATransform3DRotate(scalingAndTranslation, myRotationAngle, 0.0, 1.0, 0.0);
NSArray *keyFrameValues = [NSArray arrayWithObjects:initialTransformValue, [NSValue valueWithCATransform3D:finalTransform], nil];
CAKeyframeAnimation *myAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
myAnimation.values = keyFrameValues;
myAnimation.duration = kMyAnimationDuration;
myAnimation.delegate = self;
myAnimation.removedOnCompletion = NO;
myAnimation.fillMode = kCAFillModeForwards;
[myLayer addAnimation:myAnimation forKey:@"myAnimationKey"];
-
finalPoint
должен быть CGPoint
в координатном пространстве myLayer
.
-
kMyScalingFactor
должен быть < 1,0 для масштабирования и > 1.0 для масштабирования.
-
myRotationAngle
должен находиться в радианах. Используйте положительные значения для вращения по часовой стрелке и отрицательные значения для против часовой стрелки.
Вам также необходимо реализовать обработчик завершения анимации, чтобы сделать анимацию "stick":
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
myLayer.transform = finalTransform;
myLayer removeAnimationForKey:@"myAnimationKey"];
}
Надеюсь, что это поможет.
Ответ 3
Я получил эту работу раньше, просто анимируя представления.
1) Обложка альбома находится в сетке.
2) Переключение представления обложки альбома с использованием флип-анимации.
3) Анимируйте вид, перемещающийся по экрану.
Я быстро бросил это вместе. Предполагая, что у вас есть пустой контроллер представления и 3 вида.
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelector:@selector(transition) withObject:nil afterDelay:1];
albumArtworkSquare = [[UIView alloc] initWithFrame:CGRectMake(400, 500, 300, 300)];
albumArtworkSquare.backgroundColor = [UIColor blackColor];
[self.view addSubview:albumArtworkSquare];
frontViewOfAlbumArtwork = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
frontViewOfAlbumArtwork.backgroundColor = [UIColor blueColor];
[albumArtworkSquare addSubview:frontViewOfAlbumArtwork];
backViewToTransitionTo = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
backViewToTransitionTo.backgroundColor = [UIColor grayColor];
}
- (void)transition
{
[UIView animateWithDuration:2 animations:^{
albumArtworkSquare.frame = CGRectMake(10, 500, 300, 300);
}];
[UIView transitionFromView:frontViewOfAlbumArtwork toView:backViewToTransitionTo duration:2 options:UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL finished)
{
[frontViewOfAlbumArtwork removeFromSuperview];
[albumArtworkSquare addSubview:backViewToTransitionTo];
}];
}
Ответ 4
Ответ Mihai не обрабатывает перелистывание, как делает анимацию itunes itunes. Я немного изменил его, чтобы перевернуть вид. Вам не нужны всевозможные сумасшедшие материалы CAAnimation, всего несколько встроенных анимационных функций UIView.
#import <QuartzCore/QuartzCore.h>
@interface UIViewController (ShowModalFromView)
- (void)presentModalViewController:(UIViewController *)modalViewController fromView:(UIView *)view;
@end
@implementation UIViewController (ShowModalFromView)
- (void)presentModalViewController:(UIViewController *)modalViewController fromView:(UIView *)view
{
NSTimeInterval scaleSpeed = 0.3;
NSTimeInterval flipSpeed = 0.4;
UIView __weak *containerView = view.superview;
view.autoresizesSubviews = YES;
[self presentModalViewController:modalViewController animated:NO];
UIView __weak *presentedView = modalViewController.view.superview;
//intead of show the actual view of modalViewController, we are showing the snapshot of it to avoid layout problem
UIGraphicsBeginImageContext(presentedView.bounds.size);
[presentedView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* modalSnapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIView __weak *originalSuperView = presentedView.superview;
CGRect originalFrame = presentedView.frame;
CGRect frameInContainer = [containerView convertRect:originalFrame fromView:originalSuperView];
[presentedView removeFromSuperview];
UIImageView* snapshotView = [[UIImageView alloc] initWithImage:modalSnapshot];
snapshotView.autoresizingMask = UIViewAutoresizingNone;
[containerView bringSubviewToFront:view];
[UIView animateWithDuration:scaleSpeed delay:0 options:UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionBeginFromCurrentState animations: ^{
[UIView animateWithDuration:scaleSpeed
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations: ^{
view.frame = frameInContainer;
}
completion:nil
];
} completion:^(BOOL finished) {
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView transitionWithView:view duration:flipSpeed options:UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionTransitionFlipFromRight animations:
^{
snapshotView.frame = view.bounds;
[view addSubview:snapshotView];
} completion:^(BOOL finished) {
[originalSuperView addSubview:presentedView];
[snapshotView removeFromSuperview];
}
];
}];
}
Ответ 5
Вы можете использовать это... Для приложения на основе навигационного контроллера.
YourViewController *obj = [[YourViewController alloc]initWithNibName:@"YourViewControllerXIBName" bundle:nil];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:YES];
[self.navigationController pushViewController:obj animated:NO];
[UIView commitAnimations];
[obj release];
Ответ 6
Я делаю что-то очень похожее в одном из моих проектов, имеющих сетку обложек альбомов.
Это подход, который я принимаю. Ключ должен использовать CAAnimationGroup
.
1) Вся анимация будет включать масштабирование, вращение и перемещение по пути все в одно и то же время - сначала для слоя обложки альбома, а затем для модального слоя.
2) Анимируйте слой обложки альбома, перевернув его на 90 градусов, немного масштабируя и переместившись в предопределенное местоположение из своего текущего местоположения. В этот момент он исчезнет (по вертикали к экрану).
3) Добавьте модальный вид. Масштабирование и преобразование модального представления в точное местоположение, на котором обложка альбома расположена на шаге 1.
4) Анимация модального вида из этого положения путем масштабирования, поворота и перемещения по пути для заполнения экрана.
5) Удалите вид модальности.
6) Представьте модальное представление без анимации.
7) Выбранный путь обычно добавляет центр экрана в качестве контрольной точки. Но тогда это можно изменить в зависимости от того, как вы хотите, чтобы анимация появилась.
Ниже приведена функция, в которой вы можете увидеть использование анимационной группы. Надеюсь, это вам поможет. Я все еще не понял, как избежать обрезки анимации с помощью навигационных баров и полос вкладок.:)
+ (void)animateWithCurrentView:(UIView *)currentView
{
#define kResizeKey @"bounds.size"
#define kPathMovement @"position"
#define kRotation @"transform"
#define kGroupAnimation @"subviewBeingAnimated"
#define kLayerAnimation @"animateLayer"
//flip the view by 180 degrees in its place first.
currentView.layer.transform = CATransform3DRotate(currentView.layer.transform,radians(180), 0, 1, 0);
//set the anchor point so that the view rotates on one of its sides.
currentView.layer.anchorPoint = CGPointMake(0.0, 0.5);
/**
* Set up scaling
*/
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:kResizeKey];
//we are going to fill the screen here. So 320,480
[resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(320, 480)]];
resizeAnimation.fillMode = kCAFillModeForwards;
resizeAnimation.removedOnCompletion = NO;
/**
* Set up path movement
*/
UIBezierPath *movePath = [UIBezierPath bezierPath];
//the control point is now set to centre of the filled screen. Change this to make the path different.
CGPoint ctlPoint = CGPointMake(160.0, 240.0);
//This is the starting point of the animation. This should ideally be a function of the frame of the view to be animated. Hardcoded here.
[movePath moveToPoint:CGPointMake(320, 60)];
//The anchor point is going to end up here at the end of the animation.
[movePath addQuadCurveToPoint:CGPointMake(0, 240) controlPoint:ctlPoint];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:kPathMovement];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = YES;
/**
* Setup rotation animation
*/
CABasicAnimation* rotateAnimation = [CABasicAnimation animationWithKeyPath:kRotation];
//start from 180 degrees (done in 1st line)
CATransform3D fromTransform = CATransform3DMakeRotation(radians(180), 0, 1, 0);
//come back to 0 degrees
CATransform3D toTransform = CATransform3DMakeRotation(radians(0), 0, 1, 0);
//This is done to get some perspective.
CATransform3D persp1 = CATransform3DIdentity;
persp1.m34 = 1.0 / -3000;
fromTransform = CATransform3DConcat(fromTransform, persp1);
toTransform = CATransform3DConcat(toTransform,persp1);
rotateAnimation.toValue = [NSValue valueWithCATransform3D:toTransform];
rotateAnimation.fromValue = [NSValue valueWithCATransform3D:fromTransform];
//rotateAnimation.duration = 2;
rotateAnimation.fillMode = kCAFillModeForwards;
rotateAnimation.removedOnCompletion = NO;
/**
* Setup and add all animations to the group
*/
CAAnimationGroup *group = [CAAnimationGroup animation];
[group setAnimations:[NSArray arrayWithObjects:moveAnim,rotateAnimation, resizeAnimation, nil]];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = 0.7f;
group.delegate = self;
[group setValue:currentView forKey:kGroupAnimation];
/**
* ...and go
*/
[currentView.layer addAnimation:group forKey:kLayerAnimation];
}
Ответ 7
Если вы можете сначала получить модальное представление, чтобы показать, где вы хотите, без анимации. Должен быть пролив вперед, а установка рамки/рамки view.superview
может быть полезна в стиле листа.
Если вы это разрешите, вы можете выполнить анимацию "чистого вида", чтобы получить ее там, затем удалить представление и "представить" модальный контроллер (который управляет этим представлением), чтобы мгновенно переключить контроллер в логическое управление иерархии представлений.