iphone - texto - Implementar animación personalizada para presentar vista modal desde la vista especificada en iPad
no puedo enviar mensajes de texto desde mi iphone (7)
Estoy haciendo algo bastante similar en uno de mis proyectos con una grilla de álbum arts. Este es el enfoque que estoy tomando. La clave es usar CAAnimationGroup
.
1) Toda la animación incluiría escalar, girar y moverse a lo largo de una ruta, todo al mismo tiempo, primero para la capa de la carátula del álbum y luego para la capa de vista modal.
2) Anima la capa de carátulas del álbum volteándola 90 grados, escalando un poco y moviéndote a una ubicación predefinida desde su ubicación actual. En este punto desaparecerá (vertical a la pantalla).
3) Agregue la vista modal. Escale y transforme la vista modal para que esté en la ubicación exacta en que se posiciona la carátula del álbum en el paso 1.
4) Anima la vista modal desde esta posición escalando, girando y moviéndose a lo largo de una ruta para llenar la pantalla.
5) Elimina la vista modal.
6) Presente vista modal sin animación.
7) La ruta elegida normalmente agregaría el centro de la pantalla como punto de control. Pero eso se puede cambiar en función de cómo quiera que aparezca la animación.
A continuación se muestra una función donde puede ver el uso del grupo de animación. Espero que esto te ayude. Todavía no he descubierto cómo evitar el recorte de la animación por las barras de navegación y las barras de pestañas. :)
+ (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];
}
En el iPad, tenemos mucho más espacio para trabajar, por lo que no es ideal presentar vistas modales de pantalla completa.
Sé cómo presentar vistas modales en la nueva formSheet y se puede encontrar un enfoque más cercano en esta pregunta: iPad iTunes Animation
El problema es que no puede elegir de dónde vendrá la animación, por lo que solo está predeterminada y aparece desde el centro, quiero personalizarla para que aparezca desde una ubicación específica.
El mejor ejemplo que puedo encontrar para esta animación se puede ver en los primeros segundos de este video
Si alguien puede señalarme en la dirección correcta usando código, tutoriales o documentación, ¡lo agradecería enormemente!
Actualizar:
Después de algunas investigaciones, descubrí que esto se puede hacer usando capas y Core Animation para la primera parte; y luego animarlo con una vista modal formSheet, pero todavía no entiendo cómo lograrlo, ¡con suerte ustedes pueden ayudar!
He conseguido que esto funcione antes solo animando vistas.
1) Las ilustraciones del álbum están en una cuadrícula.
2) Transición de la vista de la ilustración del álbum usando la animación flip.
3) Anima la vista moviéndose a través de la pantalla.
Rápidamente lancé esto juntos. Suponiendo que tiene un controlador de vista vacío y 3 vistas.
- (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];
}];
}
La respuesta de Mihai no maneja el volteo ya que la animación itunes del iPad sí lo hace. Lo cambié un poco para darle la vuelta a la vista. No necesita todo tipo de cosas locas de CAAnimation, solo algunas funciones de animación incorporadas de 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];
}
];
}];
}
Lo que hice fue crear una nueva categoría para UIViewController de la siguiente manera
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''s 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
Es bastante sencillo. Por favor, hágamelo saber si esto le ayuda.
ACTUALIZAR
[UIView beginAnimations:nil context:nil];
la respuesta para usar bloques de animación en lugar de [UIView beginAnimations:nil context:nil];
/ [UIView commitAnimations]
par.
Parece que básicamente estás traduciendo (moviendo) un CALayer
mientras lo escalas hacia abajo y lo CALayer
sobre el eje y al mismo tiempo. Prueba esto:
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
debe ser unCGPoint
en el espacio de coordenadas demyLayer
. -
kMyScalingFactor
debe ser <1.0 para la reducción y> 1.0 para la ampliación. -
myRotationAngle
debe estar en radianes. Use valores positivos para girar en el sentido de las agujas del reloj y valores negativos para el sentido contrario a las agujas del reloj.
También necesita implementar un manejador de terminación de animación para hacer que la animación se "adhiera":
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
myLayer.transform = finalTransform;
myLayer removeAnimationForKey:@"myAnimationKey"];
}
Espero que esto ayude.
Puede usar esto ... Para la aplicación basada en controlador de navegación ..
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];
Si primero puede obtener la vista modal para mostrar donde lo desee sin animación. Debe ser estrecho hacia adelante y establecer los límites / marco de view.superview
puede ser útil en el estilo de hoja.
Si lo resuelve, puede realizar su animación de "vista pura" para obtenerlo, luego quitar la vista y "presentar" el controlador modal (que controla esa vista) para cambiar instantáneamente el controlador al control lógico de la vista jerarquía.