gpt4 book ai didi

ios - 如何在 iOS 上的 View 之间进行展开/收缩转换?

转载 作者:IT王子 更新时间:2023-10-29 07:42:06 27 4
gpt4 key购买 nike

我正在尝试在 iOS 中制作过渡动画,其中 View 或 View Controller 看起来会扩展以填满整个屏幕,然后在完成后收缩回原来的位置。我不确定这种过渡的正式名称是什么,但您可以在适用于 iPad 的 YouTube 应用程序中看到一个示例。当您点击网格上的其中一个搜索结果缩略图时,它会从缩略图展开,然后在您返回搜索时收缩回缩略图。

我对这两个方面感兴趣:

  1. 在一个 View 和另一个 View 之间转换时,您将如何实现这种效果?换句话说,如果 View A 占据了屏幕的某个区域,您将如何将其转换为占据整个屏幕的 View B,反之亦然?

  2. 您将如何以这种方式转换到模态视图?换句话说,如果 UIViewController C 当前正在显示并包含占据屏幕一部分的 View D,您如何让它看起来像 View D 正在变成 UIViewController E,它以模态方式呈现在 C 之上?

编辑:我正在添加赏金,看看是否能让这个问题更受欢迎。

编辑:我有一些执行此操作的源代码,Anomie 的想法非常有效,只需进行一些改进。我首先尝试为模态 Controller 的 View (E) 设置动画,但它并没有产生您正在放大屏幕的感觉,因为它没有展开 (C) 中缩略图 View 周围的所有内容。因此,我尝试为原始 Controller 的 View (C) 设置动画,但重新绘制它会导致动画不稳定,并且背景纹理之类的东西无法正确缩放。所以我最后做的是拍摄原始 View Controller (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"

@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 子类中使用它:

#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];
}
}

编辑:好吧,事实证明它并没有真正处理纵向以外的界面方向。所以我不得不切换到使用 View Controller 在 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 版本。此外,即使模态视图在前面时方向发生变化,它也能正常工作。

最佳答案

  1. 制作效果很简单。您采用全尺寸 View ,初始化其 transformcenter 以将其定位在缩略图的顶部,将其添加到适当的 super View ,然后在动画 block 中重置transformcenter 将其定位在最终位置。要关闭 View ,只需执行相反的操作:在动画 block 中设置 transformcenter 将其定位在缩略图的顶部,然后在完成 block 中将其完全删除.

    请注意,尝试从一个点(即宽度为 0 和高度为 0 的矩形)开始缩放会搞砸。如果您想这样做,请改为从宽度/高度约为 0.00001 的矩形进行缩放。

  2. 一种方法是执行与#1 中相同的操作,然后使用动画 NO 调用 presentModalViewController:animated: 以在动画完成时呈现实际的 View Controller (其中,如果操作正确,将不会因 presentModalViewController:animated: 调用而产生可见差异)。和 dismissModalViewControllerAnimated: 后跟与 #1 相同的 NO 以关闭。

    或者您可以像#1 中那样直接操作模态视图 Controller 的 View ,并接受 parentViewControllerinterfaceOrientation 和其他一些东西将无法正常工作模态视图 Controller ,因为 Apple 不支持我们创建自己的容器 View Controller 。

关于ios - 如何在 iOS 上的 View 之间进行展开/收缩转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7109707/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com