旋转设备时,动画 uiview 切换回其原始状态

Posted

技术标签:

【中文标题】旋转设备时,动画 uiview 切换回其原始状态【英文标题】:Animated uiview switches back to its original state when rotating device 【发布时间】:2014-02-14 15:30:36 【问题描述】:

我尝试在ios7下制作一个小动画。我的视图控制器有 2 个视图:一个 tableview 和一个 mapview。我有一个按钮,可以在“全屏显示地图”和“显示地图和表格视图”状态之间切换。

这是我的实验代码:

float mapNewX =  self.mapView.frame.origin.x == 0 ? 375 : 0;
float mapNewWidth = mapNewX == 0 ? self.view.frame.size.width : self.view.frame.size.width - mapNewX;

[UIView animateKeyframesWithDuration:0.1 delay:0.0 options:UIViewAnimationCurveEaseOut animations:^
 
     CGRect mapViewFrame = self.mapView.frame;
     mapViewFrame.origin.x = (mapNewX);
     mapViewFrame.size.width = mapNewWidth;
     self.mapView.frame = mapViewFrame;
 
completion:^(BOOL finished)
 
     NSLog(@"OK");
 ];

它可以工作,但是当我旋转我的设备时,它会切换回原始状态。所以,当我有完整的地图显示时,当旋转设备时,它会在屏幕上切换回 tableview + mapview。

如果我通过导航控制器从另一个视图控制器返回,也会发生同样的情况。如果我点击地图图钉的详细信息披露,它会切换回原始布局。

我该怎么办?非常感谢!

更新:这里是旋转前后的截图(一些敏感数据被屏蔽了)

【问题讨论】:

如果您使用的是 UIView 关键帧 API。您不应该也使用addKeyframeWithRelativeStartTime:relativeDuration:animations: 添加关键帧吗? 我不这么认为。感谢您的回答 你好@Tom。你使用自动布局或约束吗? 是的,我使用它们。谢谢巴舍尔。它们在界面生成器中定义。 那么你应该考虑删除它们。并再次检查问题。 :) 【参考方案1】:

如果您在旋转或屏幕导航后松动框架更改,可能与视图控制器的布局方法viewWillLayoutSubviewsviewDidLayoutSubviews 或生命周期方法viewWillAppear:viewDidAppear: 有关。如果您覆盖它们,请检查这些方法的实现。

因为您使用自动布局,所以最好为其约束设置动画而不是框架。

据我了解,您的任务是移动地图的左侧以显示下方的表格视图(打开/关闭状态)。使用自动布局,您的屏幕可能看起来像这样简单:

(UIImageView 视图起到地图的作用)

UITableView 约束

等宽约束 将超级视图约束设置为零的顶部、前导和底部空间

UIImageView 约束

将超级视图约束设置为零的顶部、底部和尾部空间 superview 的前导空间设置为表格宽度(打开状态,为此约束设置动画)

为了实现我们想要的效果,我们需要将前导空间动画化到超级视图约束(在屏幕截图上选择)。我们为此约束创建一个出口并将其constant 属性更改为打开/关闭状态。

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mapLeadingSpace;

设置菜单在屏幕上出现之前的初始状态。我们设置了默认值,但您可以从 NSUserDefaults 恢复适当的布尔设置。

- (void)viewWillAppear:(BOOL)animated

    [super viewWillAppear:animated];

    self.mapLeadingSpace.constant = 0; // default is closed

接下来,在按下条形按钮时切换动画。

- (IBAction)toggle:(id)sender

    self.mapLeadingSpace.constant = self.mapLeadingSpace.constant == 0
        ? self.tableView.frame.size.width    // opened
        : 0;                                 // closed

    [UIView animateWithDuration:.2 animations:^
        [self.imageView layoutIfNeeded];
    ];

【讨论】:

谢谢大家,但这是让我找到正确方法的答案!【参考方案2】:

使用 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation

创建一个变量来指示它是全屏地图还是拆分视图,然后在旋转完成后运行例程?不知道解决方案的技术含量有多高,但这就是我要做的! :)

【讨论】:

旋转设备时感觉不是很顺畅,谢谢您的回答 使用- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation,而不是willAnimateRotationToInterfaceOrientation:duration:。这将正确/流畅地为视图设置动画。至于切换回来的问题,就像@craig 说的那样,使用 bool 来存储打开的视图并使用它。 在这种情况下动画很流畅,我已经做到了,但主要问题仍然存在:如果我通过导航控制器从另一个视图控制器返回,布局切换回原来的。是的,我使用该属性,但同样的情况发生。可能是 aoutolayout 中的一些问题。 有一个强大的 UIButton 属性,它存储选择了哪个按钮 - 地图或地图+表格视图。当您返回 ViewWillAppear 时:只需检查选择了哪个按钮并设置选择了该按钮。当您选择按钮时,将加载相应的视图?? 是的,我也试过了,但看起来也很丑:原来的布局闪了一下,然后正确的布局就来了。【参考方案3】:

我有一个类似的拆分视图(2 个 UItableViews),这是我设法修复它的方法。

首先,有一个全局静态变量,我们称之为static BOOL _isViewSplitted 它是静态的,因此当您推送或弹出 viewContorller 时,它会保留在内存中。

覆盖didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation 在此处检查您的变量,如果是,则将它们拆分,如果否,则返回。

此外,当 viewController 弹出时,您必须检查(与上述相同)viewWillAppear

【讨论】:

谢谢,我会检查并通知您 希望它能帮助你@Tom 顺便说一句,我通过将 _isViewSplitted 存储在 NSUserDefaults 中尝试了类似的方法 如果你将它保存在 NSUserDefaults 中,那么即使在应用程序终止后你也会继续这样做。好的,所以如果您将此变量存储在 UserDefaults 中,请在您的 viewController @property(assign) BOOL isViewSplitted; 中添加属性,并在 viewWillAppear 中填充(form NsUSerDefaults)此属性,然后再进行任何拆分。 另一个问题来了:如果我点击地图图钉的详细信息披露,它会切换回原始布局:o【参考方案4】:

您可以通过对约束进行编码然后对其进行动画处理来解决此问题。

类似这样(此代码中的值与您的示例中的值不匹配):

[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[parentview setTranslatesAutoresizingMaskIntoConstraints:NO];

[parentview addSubview:subview];

NSDictionary *views = NSDictionaryOfVariableBindings(subview);
[parentview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[subview]"
                                                                   options:0
                                                                   metrics:nil
                                                                     views:views]];

NSArray *firstConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[subview]"
                                                                    options:0
                                                                    metrics:nil
                                                                      views:views];
[parentview addConstraints:firstConstraints];
[parentview layoutSubtreeIfNeeded];
[parentview removeConstraints:firstConstraints];

NSArray *secondConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-100-[subview]-100-|"
                                                                     options:0
                                                                     metrics:nil
                                                                       views:views];
[parentview addConstraints:secondConstraints];

subview.superview.wantsLayer = YES;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext* context) 
    [context setDuration:.3];
    [context setAllowsImplicitAnimation:YES];
    [context setTimingFunction: [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
    [subview layoutSubtreeIfNeeded];
 completionHandler:^
        [[self delegate] createOptionMenuForPDFViewWithInvoice:invoice andSuperView:parentview];
];

【讨论】:

【参考方案5】:

Tom,您的大问题(恕我直言)是您过于频繁地调用您在问题中提供的代码。每次它都会将地图从全屏切换到拆分,因为它是从现在的位置做出这个决定的。你为什么不检查一个布尔值 ivar?您可以在按钮操作中翻转它以切换地图全屏状态,然后在重新定向等时重复调用此代码无关紧要,只需在开始时将车削运算符中的条件更改为这个新的布尔值而不是查询地图是否全屏

【讨论】:

似乎合乎逻辑。我的问题是,我在IOS下做了几个项目,我只有一个让我感到沮丧和困惑的话题:查看布局的东西。到目前为止,我可以使用更多静态 UI-s (:)) 来解决所有问题,但现在我必须这样做。 请试一试。只需声明一个 BOOL mapViewIsFullScreen 变量并检查顶部是否有 float mapNewX = (mapViewIsFullScreen) ? .... : ..... ; Ps 你真的应该使用 CGFloat 来处理这种类型的东西而不是浮点数,因为在 arm64 上这个值实际上是一个双精度值,而不是一个浮点数【参考方案6】:

如果在原始帖子中提供了以下信息(通常,对于任何与 iOS 中的布局相关的帖子,这可能是一套很好的指南):

(1.) 故事板中是否启用了自动布局?

(2.) 您使用的是混合(混合)方法还是纯自动布局?换句话说,哪些视图的translatesAutoresizingMaskIntoConstraints 设置为YES

(3.) 如果任何布局方法或旋转回调方法被覆盖,请提供该实现

(4.) 向我们展示您的限制

我能够从 cmets 中找出#1 的答案。

鉴于我对这个问题的了解很少,我的回答是:

如果您在地图视图上使用自动布局,则在动画块中设置其框架不会永久重新定位视图,因为地图视图的约束尚未更改。相反,在动画块之前更新地图视图的约束,然后在动画块内将layoutIfNeeded 发送到self.view

// update constraints…

[UIView animateWithDuration:0.5 animations:^
    [self.view layoutIfNeeded];
];

【讨论】:

谢谢!我需要赏金,因为这对我来说是一个严肃的问题,有一段时间没有任何答案,而且我也无法理解问题到底是什么。你真的很乐意帮助我!【参考方案7】:

也可以在 Viewcontroller 类的 viewDidload 中尝试一下

if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) 
    self.edgesForExtendedLayout = UIRectEdgeNone;

【讨论】:

以上是关于旋转设备时,动画 uiview 切换回其原始状态的主要内容,如果未能解决你的问题,请参考以下文章

表格视图中的 UIView 转换

UIView 动画打破 TableView

在视图控制器之间切换时,图像旋转动画会加速吗

UIView Swift 倾斜动画

鼠标离开后CSS过渡到原始状态

UIView 旋转文本操作