전 화면을 채우기 위해 뷰 또는 뷰 컨트롤러가 확장 된 것으로 보이는 iOS에서 트랜지션 애니메이션을 만들려고하고 있습니다. 그런 다음 완료되면 다시 이전 위치로 축소됩니다. 이러한 유형의 전환이 공식적으로 불려지는지 확실하지 않지만 iPad 용 YouTube 앱에서 예를 볼 수 있습니다. 표에서 검색 결과 축소판 중 하나를 누르면 축소판에서 확장 된 다음 검색으로 돌아 가면 축소판으로 축소됩니다.iOS의보기간에 확장/계약 전환을 어떻게 만들 수 있습니까?
나는이 두 가지 측면에서 관심이 있어요 : 하나의 뷰와 다른 사이에 전환 할 때
방법이 효과를 만들 것? 즉,보기 A가 화면의 일부 영역을 차지하는 경우 전체 화면을 차지하는 B보기로 전환하려면 어떻게해야합니까?
어떻게하면이 방식으로 모달보기로 전환하겠습니까? 다시 말해서, UIViewController C가 현재 표시되어 있고 화면의 일부를 차지하는 뷰 D를 포함하고 있다면 뷰 D가 UI 상단에 모달로 나타나는 UIViewController E로 바뀌는 것처럼 보이게 할 수 있습니까?
편집 :는 나는이 질문에 더 많은 사랑을 얻을 수 있는지 확인하기 위해 현상금을 추가 해요.
편집 :이 작업을 수행하는 소스 코드가 있으며, Anomie의 아이디어는 몇 가지 개선 사항과 함께 매력적으로 작동합니다. 처음에 모달 컨트롤러보기 (E)에 애니메이션을 적용 해 보았지만 (C)의 축소판보기를 중심으로 모든 항목을 확장하지 않았기 때문에 사용자가 화면을 확대하는 것처럼 느껴지는 효과를 내지 않았습니다. 그래서 원래 컨트롤러의 뷰 (C)에 애니메이션을 적용 해 보았습니다. 그러나 재구성 된 애니메이션을 위해 다시 그릴 때 배경 텍스처와 같은 것들이 제대로 확대되지 않았습니다. 따라서 내가 수행 한 작업은 원래보기 컨트롤러 (C)의 이미지를 찍은 다음 모달 뷰 (E) 내부를 확대/축소하는 것입니다. 이 방법은 원본보다 훨씬 복잡하지만 멋지 네요! iOS가 내부 전환을 반드시 수행해야하는 방법이라고 생각합니다. 어쨌든, 여기에 UIViewController에 카테고리로 작성한 코드가 있습니다.
의 UIViewController + Transitions.h :
#import <Foundation/Foundation.h>
@interface UIViewController (Transitions)
// make a transition that looks like a modal view
// is expanding from a subview
- (void)expandView:(UIView *)sourceView
toModalViewController:(UIViewController *)modalViewController;
// make a transition that looks like the current modal view
// is shrinking into a subview
- (void)dismissModalViewControllerToView:(UIView *)view;
@end
의 UIViewController + Transitions.m :
#import "UIViewController+Transitions.h"
...
- (void)userDidTapThumbnail {
DetailViewController *detail =
[[DetailViewController alloc]
initWithNibName:nil bundle:nil];
[self expandView:thumbnailView toModalViewController:detail];
[detail release];
}
- (void)dismissModalViewControllerAnimated:(BOOL)animated {
if (([self.modalViewController isKindOfClass:[DetailViewController class]]) &&
(animated)) {
[self dismissModalViewControllerToView:thumbnailView];
}
else {
[super dismissModalViewControllerAnimated:animated];
}
}
:
#import "UIViewController+Transitions.h"
@implementation UIViewController (Transitions)
// capture a screen-sized image of the receiver
- (UIImageView *)imageViewFromScreen {
// make a bitmap copy of the screen
UIGraphicsBeginImageContextWithOptions(
[UIScreen mainScreen].bounds.size, YES,
[UIScreen mainScreen].scale);
// get the root layer
CALayer *layer = self.view.layer;
while(layer.superlayer) {
layer = layer.superlayer;
}
// render it into the bitmap
[layer renderInContext:UIGraphicsGetCurrentContext()];
// get the image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// close the context
UIGraphicsEndImageContext();
// make a view for the image
UIImageView *imageView =
[[[UIImageView alloc] initWithImage:image]
autorelease];
return(imageView);
}
// make a transform that causes the given subview to fill the screen
// (when applied to an image of the screen)
- (CATransform3D)transformToFillScreenWithSubview:(UIView *)sourceView {
// get the root view
UIView *rootView = sourceView;
while (rootView.superview) rootView = rootView.superview;
// convert the source view's center and size into the coordinate
// system of the root view
CGRect sourceRect = [sourceView convertRect:sourceView.bounds toView:rootView];
CGPoint sourceCenter = CGPointMake(
CGRectGetMidX(sourceRect), CGRectGetMidY(sourceRect));
CGSize sourceSize = sourceRect.size;
// get the size and position we're expanding it to
CGRect screenBounds = [UIScreen mainScreen].bounds;
CGPoint targetCenter = CGPointMake(
CGRectGetMidX(screenBounds),
CGRectGetMidY(screenBounds));
CGSize targetSize = screenBounds.size;
// scale so that the view fills the screen
CATransform3D t = CATransform3DIdentity;
CGFloat sourceAspect = sourceSize.width/sourceSize.height;
CGFloat targetAspect = targetSize.width/targetSize.height;
CGFloat scale = 1.0;
if (sourceAspect > targetAspect)
scale = targetSize.width/sourceSize.width;
else
scale = targetSize.height/sourceSize.height;
t = CATransform3DScale(t, scale, scale, 1.0);
// compensate for the status bar in the screen image
CGFloat statusBarAdjustment =
(([UIApplication sharedApplication].statusBarFrame.size.height/2.0)
/scale);
// transform to center the view
t = CATransform3DTranslate(t,
(targetCenter.x - sourceCenter.x),
(targetCenter.y - sourceCenter.y) + statusBarAdjustment,
0.0);
return(t);
}
- (void)expandView:(UIView *)sourceView
toModalViewController:(UIViewController *)modalViewController {
// get an image of the screen
UIImageView *imageView = [self imageViewFromScreen];
// insert it into the modal view's hierarchy
[self presentModalViewController:modalViewController animated:NO];
UIView *rootView = modalViewController.view;
while (rootView.superview) rootView = rootView.superview;
[rootView addSubview:imageView];
// make a transform that makes the source view fill the screen
CATransform3D t = [self transformToFillScreenWithSubview:sourceView];
// animate the transform
[UIView animateWithDuration:0.4
animations:^(void) {
imageView.layer.transform = t;
} completion:^(BOOL finished) {
[imageView removeFromSuperview];
}];
}
- (void)dismissModalViewControllerToView:(UIView *)view {
// take a snapshot of the current screen
UIImageView *imageView = [self imageViewFromScreen];
// insert it into the root view
UIView *rootView = self.view;
while (rootView.superview) rootView = rootView.superview;
[rootView addSubview:imageView];
// make the subview initially fill the screen
imageView.layer.transform = [self transformToFillScreenWithSubview:view];
// remove the modal view
[self dismissModalViewControllerAnimated:NO];
// animate the screen shrinking back to normal
[UIView animateWithDuration:0.4
animations:^(void) {
imageView.layer.transform = CATransform3DIdentity;
}
completion:^(BOOL finished) {
[imageView removeFromSuperview];
}];
}
@end
당신이 UIViewController 하위에이 같은 것을 사용할 수 있습니다 편집 : 음, 그게 밝혀 지 않습니다 초상화 이외의 인터페이스 방향을 실제로 처리하지 못합니다. 그래서 뷰 컨트롤러를 사용하여 회전을 전달하는 UIWindow에서 전환을 애니메이션으로 전환해야했습니다.
의 UIViewController + Transitions.m :
@interface ContainerViewController : UIViewController { }
@end
@implementation ContainerViewController
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation {
return(YES);
}
@end
...
// get the screen size, compensating for orientation
- (CGSize)screenSize {
// get the size of the screen (swapping dimensions for other orientations)
CGSize size = [UIScreen mainScreen].bounds.size;
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
CGFloat width = size.width;
size.width = size.height;
size.height = width;
}
return(size);
}
// capture a screen-sized image of the receiver
- (UIImageView *)imageViewFromScreen {
// get the root layer
CALayer *layer = self.view.layer;
while(layer.superlayer) {
layer = layer.superlayer;
}
// get the size of the bitmap
CGSize size = [self screenSize];
// make a bitmap to copy the screen into
UIGraphicsBeginImageContextWithOptions(
size, YES,
[UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
// compensate for orientation
if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
CGContextTranslateCTM(context, size.width, 0);
CGContextRotateCTM(context, M_PI_2);
}
else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
CGContextTranslateCTM(context, 0, size.height);
CGContextRotateCTM(context, - M_PI_2);
}
else if (self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
CGContextTranslateCTM(context, size.width, size.height);
CGContextRotateCTM(context, M_PI);
}
// render the layer into the bitmap
[layer renderInContext:context];
// get the image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// close the context
UIGraphicsEndImageContext();
// make a view for the image
UIImageView *imageView =
[[[UIImageView alloc] initWithImage:image]
autorelease];
// done
return(imageView);
}
// make a transform that causes the given subview to fill the screen
// (when applied to an image of the screen)
- (CATransform3D)transformToFillScreenWithSubview:(UIView *)sourceView
includeStatusBar:(BOOL)includeStatusBar {
// get the root view
UIView *rootView = sourceView;
while (rootView.superview) rootView = rootView.superview;
// by default, zoom from the view's bounds
CGRect sourceRect = sourceView.bounds;
// convert the source view's center and size into the coordinate
// system of the root view
sourceRect = [sourceView convertRect:sourceRect toView:rootView];
CGPoint sourceCenter = CGPointMake(
CGRectGetMidX(sourceRect), CGRectGetMidY(sourceRect));
CGSize sourceSize = sourceRect.size;
// get the size and position we're expanding it to
CGSize targetSize = [self screenSize];
CGPoint targetCenter = CGPointMake(
targetSize.width/2.0,
targetSize.height/2.0);
// scale so that the view fills the screen
CATransform3D t = CATransform3DIdentity;
CGFloat sourceAspect = sourceSize.width/sourceSize.height;
CGFloat targetAspect = targetSize.width/targetSize.height;
CGFloat scale = 1.0;
if (sourceAspect > targetAspect)
scale = targetSize.width/sourceSize.width;
else
scale = targetSize.height/sourceSize.height;
t = CATransform3DScale(t, scale, scale, 1.0);
// compensate for the status bar in the screen image
CGFloat statusBarAdjustment = includeStatusBar ?
(([UIApplication sharedApplication].statusBarFrame.size.height/2.0)
/scale) : 0.0;
// transform to center the view
t = CATransform3DTranslate(t,
(targetCenter.x - sourceCenter.x),
(targetCenter.y - sourceCenter.y) + statusBarAdjustment,
0.0);
return(t);
}
- (void)expandView:(UIView *)sourceView
toModalViewController:(UIViewController *)modalViewController {
// get an image of the screen
UIImageView *imageView = [self imageViewFromScreen];
// show the modal view
[self presentModalViewController:modalViewController animated:NO];
// make a window to display the transition on top of everything else
UIWindow *window =
[[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.hidden = NO;
window.backgroundColor = [UIColor blackColor];
// make a view controller to display the image in
ContainerViewController *vc = [[ContainerViewController alloc] init];
vc.wantsFullScreenLayout = YES;
// show the window
[window setRootViewController:vc];
[window makeKeyAndVisible];
// add the image to the window
[vc.view addSubview:imageView];
// make a transform that makes the source view fill the screen
CATransform3D t = [self
transformToFillScreenWithSubview:sourceView
includeStatusBar:(! modalViewController.wantsFullScreenLayout)];
// animate the transform
[UIView animateWithDuration:0.4
animations:^(void) {
imageView.layer.transform = t;
} completion:^(BOOL finished) {
// we're going to crossfade, so change the background to clear
window.backgroundColor = [UIColor clearColor];
// do a little crossfade
[UIView animateWithDuration:0.25
animations:^(void) {
imageView.alpha = 0.0;
}
completion:^(BOOL finished) {
window.hidden = YES;
[window release];
[vc release];
}];
}];
}
- (void)dismissModalViewControllerToView:(UIView *)view {
// temporarily remove the modal dialog so we can get an accurate screenshot
// with orientation applied
UIViewController *modalViewController = [self.modalViewController retain];
[self dismissModalViewControllerAnimated:NO];
// capture the screen
UIImageView *imageView = [self imageViewFromScreen];
// put the modal view controller back
[self presentModalViewController:modalViewController animated:NO];
[modalViewController release];
// make a window to display the transition on top of everything else
UIWindow *window =
[[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.hidden = NO;
window.backgroundColor = [UIColor clearColor];
// make a view controller to display the image in
ContainerViewController *vc = [[ContainerViewController alloc] init];
vc.wantsFullScreenLayout = YES;
// show the window
[window setRootViewController:vc];
[window makeKeyAndVisible];
// add the image to the window
[vc.view addSubview:imageView];
// make the subview initially fill the screen
imageView.layer.transform = [self
transformToFillScreenWithSubview:view
includeStatusBar:(! self.modalViewController.wantsFullScreenLayout)];
// animate a little crossfade
imageView.alpha = 0.0;
[UIView animateWithDuration:0.15
animations:^(void) {
imageView.alpha = 1.0;
}
completion:^(BOOL finished) {
// remove the modal view
[self dismissModalViewControllerAnimated:NO];
// set the background so the real screen won't show through
window.backgroundColor = [UIColor blackColor];
// animate the screen shrinking back to normal
[UIView animateWithDuration:0.4
animations:^(void) {
imageView.layer.transform = CATransform3DIdentity;
}
completion:^(BOOL finished) {
// hide the transition stuff
window.hidden = YES;
[window release];
[vc release];
}];
}];
}
휴 아래 훨씬 더 복잡 버전을 참조하십시오! 그러나 이제는 제한된 API를 사용하지 않고 Apple 버전과 거의 비슷하게 보입니다. 또한 모달 뷰가 정면에있는 동안 방향이 변경 되더라도 작동합니다.
감사합니다. 우리가 부분 2에서 애니메이션을 적용 할 것인지에 대해서는 분명하지 않다. 모달 뷰 컨트롤러가 뷰에 모달 표시되기 전에 애니메이션을 적용 할 수 있습니까? 이 경우 현재보기 컨트롤러보기의 하위보기로 추가해야합니다. 나는 이것을위한 약간의 소스 코드를 보는 것에 매우 흥미가있을 것이다. –
@ JesseCrossen : # 2에서 첫 번째 방법은 모달 뷰 컨트롤러의 뷰와 같은 일부 뷰를 애니메이션으로 만들 것입니다. CALayer의'renderInContext :'를 사용하여 만든 "스크린 샷"일 수 있습니다. 두 번째 방법은 뷰 컨트롤러의 뷰를 직접 조작하는 것입니다. – Anomie
설명해 주셔서 감사합니다. 나는 뷰 컨트롤러의 비표준 조작이 실제로 실용적 일 수 있다는 것을 발견했다. 그래서 나는 소스 코드에 대해 물었다.어떻게 작동하는지 알려주고 알려 드리겠습니다. –