【问题标题】:Fake push and pop animation when replace UIView替换 UIView 时的假推送和弹出动画
【发布时间】:2015-06-03 15:26:25
【问题描述】:

由于某些原因,我必须使用addSubviewaddChildViewController 来替换视图而不是推送/弹出视图控制器。问题是我想在更改UIViewController(推送/弹出)时准确地伪造​​动画。 这是我的尝试:

RootViewController.m

// switch controller view
currentController = nextViewController;

[self addChildViewController:currentController]; 
[self.view addSubview:currentController.view];

// pop - move down
[currentController.view setFrame:CGRectMake(self.view.frame.size.width, 0, currentController.view.frame.size.width, currentController.view.frame.size.height)];

        [UIView animateWithDuration:0.5
                         animations:^{
                             [currentController.view setFrame:CGRectMake(self.view.frame.size.width, self.view.frame.size.height, currentController.view.frame.size.width, currentController.view.frame.size.height)];
                             [self addConstrainForView];
                         }];


//Push - move up
  [currentController.view setFrame:CGRectMake(0, self.view.frame.size.height, currentController.view.frame.size.width, currentController.view.frame.size.height)];
        [UIView animateWithDuration:0.5
                         animations:^{
                             [currentController.view setFrame:CGRectMake(0, 0, currentController.view.frame.size.width, currentController.view.frame.size.height)];
                             [self addConstrainForView];
                         }];

这段代码不能作为切面工作,因为下一个控制器从下到上移动(推),当它移动时,后面有一个空白背景(根控制器的背景)。

所以我需要正确的方法来伪造推送和弹出动画(上拉和下拉)。
任何帮助将不胜感激。

【问题讨论】:

  • 您是否注意到您首先设置了框架,然后在弹出窗口时将动画设置为同一框架 - 向下移动?
  • 我以前做过。没有时间写一个完整的答案,但你必须拍摄前一个屏幕的图像,然后在你开始动画之前把它放在下一个屏幕上,这样它看起来就像一个真正的 UIViewController。我将它用于 iPad 上的嵌入式列表,让 iPhone 感觉您在 iPad 上滚动列表。唯一需要注意的是,至少在我的实现中,您必须考虑屏幕的状态,如果它滚动或者在您启动动画时看起来像是在晃动。
  • 你的 pop 不应该从 0,0 到 0,height 吗?你似乎从宽度,0 到宽度,高度。这不都是屏幕外的吗?
  • @MichaelOlenick 你能分享一些代码和平吗?我真的不知道该怎么做。
  • @RoryMcKinnel 在这种情况下我只需要移动 y 轴。另外,我提到的问题是做动画时背后黑屏

标签: ios objective-c uiview


【解决方案1】:

我在一个应用程序中使用了这个效果。我想要(并且拥有)嵌入到 iPad 应用程序边缘的 iPhone 系列选择屏幕的外观。因此,用户从列表中按下一个按钮,然后屏幕转换,看起来像 UIViewController 前进,到更多选项,尽管它都在边缘的窗口中(并且相关内容会在右侧弹出)。这是我使用的代码:

- (void)advanceLevel
{
    UIImageView *screenShot = [self getTableScreenShot];
    [self.tableView.superview addSubview:screenShot];

    // Put screen shot over the whole thing with mask for iPhone style animation
    TableViewMask *mask =
            [[TableViewMask alloc] initWithFrame:CGRectMake(0, 0, 1024, 768) withImage:[Util TakeScreenshot]];
    [self.tableView.superview addSubview:mask];

    tableLevel++;
    [self updateData];

    [UIView animateWithDuration:.6 animations:^
    {
        screenShot.frame =
        CGRectMake(self.tableView.frame.origin.x-self.tableView.frame.size.width, self.tableView.frame.origin.y,
                   self.tableView.frame.size.width, self.tableView.frame.size.height);
        controller.toolbar.backButton.alpha = (tableLevel>1?1:0);
    }
    completion:^(BOOL finished)
    {
        [screenShot removeFromSuperview];
        [mask removeFromSuperview];
        [self updateUI];
    }];
}

- (void)goBackLevel
{
    if(tableLevel==1){ return; }

    if(tableLevel==3 && isEditing==YES)
    {
        // Skip this screen if coming from higher screens
        tableLevel--;
    }

    UIImageView *screenShot = [self getTableScreenShot];
    [self.tableView.superview addSubview:screenShot];

    // Put screen shot over the whole thing with mask for iPhone style animation
    TableViewMask *mask = [[TableViewMask alloc] initWithFrame:CGRectMake(0, 0, 1024, 768) withImage:[Util TakeScreenshot]];
    [self.tableView.superview addSubview:mask];

    tableLevel--;
    [self updateData];

    [UIView animateWithDuration:.6 animations:^
    {
        screenShot.frame =
            CGRectMake(self.tableView.frame.origin.x+self.tableView.frame.size.width, self.tableView.frame.origin.y,
                       self.tableView.frame.size.width, self.tableView.frame.size.height);
        controller.toolbar.backButton.alpha = (tableLevel>1?1:0);
    }
    completion:^(BOOL finished)
    {
        [screenShot removeFromSuperview];
        [mask removeFromSuperview];
        [self updateUI];
    }];
}

...这是屏幕截图:

+ (UIImage *)TakeScreenshot
{
    CGSize imageSize = [[UIScreen mainScreen] bounds].size;

    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);

    CGContextRef context = UIGraphicsGetCurrentContext();

    // Iterate over every window from back to front
    for (UIWindow *window in [[UIApplication sharedApplication] windows])
    {
        if (![window respondsToSelector:@selector(screen)] || [window screen] == [UIScreen mainScreen])
        {
            // -renderInContext: renders in the coordinate space of the layer,
            // so we must first apply the layer's geometry to the graphics context
            CGContextSaveGState(context);
            // Center the context around the window's anchor point
            CGContextTranslateCTM(context, [window center].x, [window center].y);
            // Apply the window's transform about the anchor point
            CGContextConcatCTM(context, [window transform]);
            // Offset by the portion of the bounds left of and above the anchor point
            CGContextTranslateCTM(context,
                                  -[window bounds].size.width * [[window layer] anchorPoint].x,
                                  -[window bounds].size.height * [[window layer] anchorPoint].y);

            // Render the layer hierarchy to the current context
            [[window layer] renderInContext:context];

            // Restore the context
            CGContextRestoreGState(context);
        }
    }

    // Retrieve the screenshot image
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return image;
}

.. 最后是 TableViewMask:

@implementation TableViewMask

UIImage *screenShotImage;
- (instancetype)initWithFrame:(CGRect)frame withImage:(UIImage *)_screenShotImage
{
    self = [super initWithFrame:frame];
    if(self)
    {
        self.backgroundColor = [UIColor clearColor];

        screenShotImage = _screenShotImage;

        [self setNeedsDisplay];
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    [screenShotImage drawInRect:rect];

    CGRect intersection = CGRectIntersection( CGRectMake(10.2, 130, 299.6, 600), rect );
    if( CGRectIntersectsRect( intersection, rect ) )
    {
        CGContextFillRect(context, CGRectMake(10, 130, 300, 600));
        CGContextClearRect(context, intersection);
        CGContextSetFillColorWithColor( context, [UIColor clearColor].CGColor );
        CGContextFillRect( context, intersection);
    }
}

@end

我留下了一些我独有的东西,因为您可能需要类似的电话。例如 [self updateData] 会考虑系统的状态来更新模型并设置任何其他基于状态的调用。当一个屏幕应该被跳过时还有一个例外,我已经离开了。你可以安全地删除它们,尽管你可能需要类似的变体(因为你并没有真正改变 UIViewController 的)。

对此我唯一要注意的是,除非你有像我这样的角落案例——在 iPad 或类似的东西上模仿 iPhone 的弹出视图——使用真正的 UIVewController 可能会更好。

【讨论】:

    【解决方案2】:

    这篇文章似乎与您正在尝试做的事情相关?

    When to use addChildViewController vs pushViewController

    似乎建议您可以使用 transitionFromViewController:toViewController:duration:options:animations:completion: 在您自己管理的控制器之间设置动画过渡。

    【讨论】:

    • 尝试了很多方法,总是崩溃:子视图控制器和调用时必须有一个共同的父视图控制器 -[UIViewController transitionFromViewController:toViewController:duration:options:animations:completion
    • 这个机制似乎是你应该做的。您需要一些步骤,例如为新的调用didMoveToParentViewController 和在旧的调用willMoveFromParentViewController。发现这个问题的答案似乎更完整:stackoverflow.com/questions/8453653/…
    猜你喜欢
    • 1970-01-01
    • 2011-09-24
    • 2019-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-02
    • 2011-08-17
    相关资源
    最近更新 更多