基于尺寸元素的设备旋转期间的动画更改将在旋转完成后进行

Posted

技术标签:

【中文标题】基于尺寸元素的设备旋转期间的动画更改将在旋转完成后进行【英文标题】:Animate changes during device rotation based on size element will be after rotation completes 【发布时间】:2015-02-01 03:42:00 【问题描述】:

我有一个动态的按钮设置,可以根据故事板中设置的自动布局约束自动调整宽度和高度。在纵向时,按钮具有相同的宽度和高度,因此框架是完美的方形,而当设备旋转为横向时,按钮会变得更短和更宽。我在按钮的图层上设置了cornerRadius,因此它们在纵向时是完美的圆形并且效果很好,但是当我旋转到横向时,角半径显然不再正确。我需要改变它,使它变成一个椭圆形。问题是,无论我尝试将该代码放在哪里,我都无法获得旋转发生后按钮的正确宽度和高度。我希望在设备旋转时发生这种情况 - 不想等到旋转完成。理想情况下,cornerRadius 会在转换动画的同时动画变化。

如果我使用viewWillLayoutSubviewsviewDidLayoutSubviews,它会在启动应用程序时正确获取按钮框架,但在旋转到横向时,会在按钮框架更新为新方向之前调用此方法,所以我可以' t 计算正确的圆角半径。

如果我使用viewWillTransitionToSize:withTransitionCoordinator:willTransitionToTraitCollection:withTransitionCoordinator:willRotateToInterfaceOrientation,则在启动应用程序时不会调用它,并且在旋转设备时会在框架更新为新尺寸之前调用它。

如果我使用willAnimateRotationToInterfaceOrientation,则在启动应用程序时不会调用它,但在旋转设备时它会正确获取新的按钮框架。但这种方法已被弃用。

那么问题来了,你可以使用什么方法来根据按钮在旋转完成时的大小来设置按钮的属性,即在旋转完成之前调用?

请注意,每次方向更改都需要调用它,而不仅仅是尺寸等级更改(旋转 iPad 不会更改尺寸等级)。我只需要支持ios 8.0+。

这是我在方法中放置的代码,以了解其大小是否正确:

println("\(button.frame.size.width) x \(button.frame.size.height)")

【问题讨论】:

【参考方案1】:

下面我概述了如何使用CADisplayLink 在动画的每一帧期间更新属性,但有一种更简单的方法。使用更新角半径的layoutSubviews 子类化按钮(或视图或其他):

class RoundedCornerView: UIView 

    override func layoutSubviews() 
        super.layoutSubviews()

        layer.cornerRadius = min(bounds.width, bounds.height) / 2
    


然后,在动画过程中,随着帧大小的变化,圆角半径会自动更新:


正如 Joey 指出的那样,可以使用viewWillTransitionToSize 来通知新尺寸,然后使用animateAlongsideTransition 来协调您的附加动画和主动画。

如果您想为不可动画的属性设置动画,例如圆角半径(无论如何,在基于块的动画中),您可以在动画期间使用显示链接。这有效地为旋转动画的每一帧调用了我们的方法。因此,我们将有一个方法来查看表示层(它向您显示层中间动画的当前状态)以动态调整角半径。您也可以为此使用animateAlongsideTransition,但在这种情况下我们没有使用animation 闭包,而是仅使用completion 闭包来了解动画何时完成,因此可以停止显示链接:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) 
    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    let displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
    displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)

    coordinator.animateAlongsideTransition(nil)  (context) -> Void in
        displayLink.invalidate()
    


func handleDisplayLink(displayLink: CADisplayLink) 
    updateButtonsCornerRadius()


func updateButtonsCornerRadius() 
    for button in [button1, button2] 
        let presentationLayer = button.layer.presentationLayer() as CALayer
        let minDimension = min(presentationLayer.frame.size.width, presentationLayer.frame.size.height)
        button.layer.cornerRadius = minDimension / 2.0
    

坦率地说,我不确定这个圆角半径动画是否足以证明这个显示链接的合理性(我不得不在模拟器中使用 command+T 来减慢动画速度欣赏差异),但这说明了基本思想。

【讨论】:

【参考方案2】:

答案是使用viewWillTransitionToSize:withTransitionCoordinator:,将您的代码放在UIViewControllerTransitionCoordinatoranimateAlongsideTransition: 方法的动画闭包中。这将允许您根据旋转完成时的大小为更改设置动画。

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) 
    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    coordinator.animateAlongsideTransition( (context: UIViewControllerTransitionCoordinatorContext!) -> Void in
        self.updateButtonsCornerRadius()
    , completion: nil)

虽然应用启动时不会调用此方法,但您也需要在 viewDidLoad 中调用 self.updateButtonsCornerRadius()

【讨论】:

以上是关于基于尺寸元素的设备旋转期间的动画更改将在旋转完成后进行的主要内容,如果未能解决你的问题,请参考以下文章

无法启动比旋转持续时间更长的基于旋转的动画?

旋转图像。动画列表或动画旋转? (安卓)

设备旋转后动画点重新定位

UICollectionView 在旋转时重新排列单元格

更改设备方向时如何为uiimageview设置动画

为啥绘图路径的笔画会随着设备旋转动画而变形