【问题标题】:How do you update UIView layout when using UIKit Dynamics?使用 UIKit Dynamics 时如何更新 UIView 布局?
【发布时间】:2016-02-22 06:48:38
【问题描述】:

我有一个自定义 UIView,它使用 UIKit Dynamics 在用户点击按钮时执行动画。有问题的视图很简单,我在layoutSubviews() 中手动布局。然而,在 UIKit Dynamics 运行时,每帧动画都会调用 layoutSubviews(),而我在此期间所做的任何布局更改(例如,响应更高的状态栏)都会导致我的动态视图失真。

如何在 UIKit Dynamics 动画进行时响应视图大小的变化?

更新

我创建了一个演示项目(它与我的用例非常匹配,尽管它被精简了),并将其发布到on GitHub。故事板使用 AutoLayout,但视图选择退出 AutoLayout 以使用 translatesAutoresizingMaskIntoConstraints = false 布置自己的子视图。要重现该行为,请在模拟器中运行(我选择 iPhone 5),然后在星星摆动时按 ⌘Y 以见证失真。这是视图代码:

import UIKit

class CustomView: UIView {

    var swingingView: UIView!

    var animator: UIDynamicAnimator!
    var attachment: UIAttachmentBehavior!

    var lastViewFrame = CGRectZero


    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.translatesAutoresizingMaskIntoConstraints = false

        swingingView = UIImageView(image: UIImage(named: "Star"))
        self.addSubview(swingingView)
    }

    override func layoutSubviews() {
        // Don't run for every frame of the animation. Only when responding to a layout change
        guard self.frame != lastViewFrame else {
            return
        }

        lastViewFrame = self.frame

        swingingView.frame = CGRect(x: 0, y: self.frame.size.height / 2, width: 100, height: 100)

        // Only run this setup code once
        if animator == nil {
            animator = UIDynamicAnimator(referenceView: self)

            let gravity = UIGravityBehavior(items: [swingingView])
            gravity.magnitude = 1.5
            animator.addBehavior(gravity)

            attachment = UIAttachmentBehavior(item: swingingView,
                offsetFromCenter: UIOffset(horizontal: 0, vertical: swingingView.frame.size.height / -2),
                attachedToAnchor: CGPoint(x: self.bounds.size.width / 2, y: 0))
            attachment.length = CGFloat(250.0)
            animator.addBehavior(attachment)
        }

        animator.updateItemUsingCurrentState(swingingView)
    }
}

【问题讨论】:

  • 您是否尝试过创建 UIViewController 的自定义实例并将动画和布局放入 viewDidLoad() 中?这将解决您的布局代码被多次触发的问题。
  • 你能把代码放在这里吗?我认为可能发生的事情是您的 autoLayout 是基于您的动画视图,因此当它动画时,会调用 updateConstraints 并且它会根据动画扭曲您的视图。如果是这种情况,请确保您的其他视图约束基于布局指南边距而不是动画视图
  • 好吧,如果您使用变量来设置帧,我想最好的办法是调试。也许在layoutSubviews() 中设置帧的位置设置断点,然后查看哪些值正在发生变化。恐怕没有代码我帮不上什么忙。
  • @MatthewLawrenceBailey 我在问题中添加了一个代码示例,并在 GitHub 上添加了一个完整项目:github.com/abbeycode/LayoutDuringDynamics

标签: ios swift user-interface layout uikit-dynamics


【解决方案1】:

您应该使用func updateItemUsingCurrentState(_ item: UIDynamicItem)UIDynamicAnimator class reference

动态动画师会自动读取初始状态(位置和 您添加到其中的每个动态项目的旋转),然后采用 负责更新项目的状态。如果你主动改变 将动态项添加到动态项后的状态 动画师,调用此方法要求动画师阅读并合并 新的状态。

【讨论】:

  • 这是一个很好的提示,但是如果在旋转动画期间布局发生变化,我的视图仍然会变形。我什至尝试将视图的transform 设置为身份,但这也不起作用。但是,使用这种方法,视图只会被扭曲一次,并保持这种状态。以前,它会在整个动画中变形。我会尝试一起制作一个示例项目。
  • 我在问题中添加了一个代码示例,并在 GitHub 上添加了一个完整项目:github.com/abbeycode/LayoutDuringDynamics
【解决方案2】:

问题是您正在重新设置已旋转视图的frame(即应用了变换)。 frame 是父上下文中视图的大小。因此您无意中更改了此图像视图的bounds

由于您使用.ScaleToFill 的默认内容模式,因此当图像视图的bounds 更改时,星形会变得(进一步)扭曲,这使问题更加复杂。 (注意,图片一开始不是方形的,所以我个人会使用.ScaleAspectFit,但这取决于你。)

无论如何,您应该能够通过 (a) 在您首次将 UIImageView 添加到视图层次结构时设置 frame 来解决此问题; (b) 不要更改layoutSubviews中的frame,而只是调整图像视图的center

【讨论】:

  • 谢谢,但调整框架是故意的。在我的实际应用程序中,我希望图像在视图缩小时实际缩小一点。有没有办法做到这一点?
  • 当然,只需更改boundsbounds 在子视图的坐标系内。 frame 在超级视图的坐标系内(应用变换后)。
  • 所以我应该更改boundscenter,这与更改frame 的效果不同吗?我试试看。
  • 正确,设置boundscenter 与更改frame 完全不同。如果您更改frame,则行为现在是恰好应用于视图的transform 的旋转量的函数(这就是为什么您会看到失真并且行为会根据视图的多少而改变重置frame 时旋转)。我想,如果你真的想要,你可以暂时将transform 重置回身份,然后设置frame 可能会起作用,但我不确定你为什么要这样做。
  • @Rob 我在这里提出了一个相关问题 (stackoverflow.com/questions/45339297/…)。如果您能看一看,将非常感激。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-07
  • 2014-12-03
  • 1970-01-01
  • 2021-09-23
  • 2015-01-21
  • 1970-01-01
相关资源
最近更新 更多