gpt4 book ai didi

ios - 使用 AutoLayout 调整圆形(圆形)UIView 的大小...如何在调整大小动画期间为cornerRadius 设置动画?

转载 作者:技术小花猫 更新时间:2023-10-29 10:19:21 27 4
gpt4 key购买 nike

我有一个子类 UIView,我们可以调用它 CircleView . CircleView 自动将角半径设置为其宽度的一半,以便它成为一个圆。

问题是当“CircleView”被 AutoLayout 约束调整大小时......例如 关于设备旋转 ...在调整大小之前它会严重扭曲,因为“cornerRadius”属性必须 catch ,并且操作系统只向 View 的框架发送一个“边界”更改。

我想知道是否有人有一个好的、清晰的策略来以在这种情况下不会扭曲的方式实现“CircleView”,但仍将其内容屏蔽为圆形,并允许在所述 UIView 周围存在边框。

最佳答案

更新:如果您的部署目标是 iOS 11 或更高版本:

从 iOS 11 开始,UIKit 将动画 cornerRadius如果您在动画块内更新它。只需设置您的 View layer.cornerRadiusUIView动画块,或者(为了处理界面方向变化),在 layoutSubviews 中设置它或 viewDidLayoutSubviews .

原文:如果您的部署目标早于 iOS 11:

所以你想要这个:

smoothly resizing circle view

(我打开了调试 > 慢速动画以使平滑度更容易看到。)

旁白,随意跳过这一段:事实证明这比应该的要困难得多,因为 iOS SDK 并没有以方便的方式提供自转动画的参数(持续时间、时间曲线)。你可以(我认为)通过覆盖 -viewWillTransitionToSize:withTransitionCoordinator: 来获得它们。在您的 View Controller 上调用 -animateAlongsideTransition:completion:在转换协调器上,并在您传递的回调中,获取 transitionDurationcompletionCurve来自 UIViewControllerTransitionCoordinatorContext .然后您需要将该信息传递给您的 CircleView , 必须保存它(因为它还没有调整大小!)稍后它收到 layoutSubviews ,它可以用它来创建一个 CABasicAnimationcornerRadius使用那些保存的动画参数。并且不要在时不小心创建动画不是 动画调整大小... 旁白结束。

哇,这听起来像是大量的工作,您必须涉及到 View Controller 。这是另一种完全在 CircleView 内部实现的方法.它现在可以工作(在 iOS 9 中),但我不能保证它将来总是可以工作,因为它做出了两个理论上可能在 future 错误的假设。

这是方法:覆盖 -actionForLayer:forKey:CircleView返回一个 Action ,当运行时,为 cornerRadius 安装动画.

这是两个假设:

  • bounds.originbounds.size获得单独的动画。 (现在确实如此,但据推测, future 的 iOS 可以为 bounds 使用单个动画。如果没有找到 bounds 动画,检查 bounds.size 动画就很容易了。)
  • bounds.size在 Core Animation 请求 cornerRadius 之前将动画添加到层行动。

  • 鉴于这些假设,当 Core Animation 要求提供 cornerRadius 时行动,我们可以得到 bounds.size从图层中创建动画,复制它,然后修改副本以制作动画 cornerRadius反而。副本具有与原件相同的动画参数(除非我们修改它们),因此它具有正确的持续时间和时序曲线。

    这是 CircleView 的开始:
    class CircleView: UIView {

    override func layoutSubviews() {
    super.layoutSubviews()
    updateCornerRadius()
    }

    private func updateCornerRadius() {
    layer.cornerRadius = min(bounds.width, bounds.height) / 2
    }

    请注意, View 的边界是在 View 接收 layoutSubviews 之前设置的。 ,因此在我们更新 cornerRadius 之前.这就是为什么 bounds.size动画安装在 cornerRadius之前请求动画。每个属性的动画都安装在属性的 setter 中。

    当我们设置 cornerRadius , Core Animation 向我们索要 CAAction运行它:
        override func action(for layer: CALayer, forKey event: String) -> CAAction? {
    if event == "cornerRadius" {
    if let boundsAnimation = layer.animation(forKey: "bounds.size") as? CABasicAnimation {
    let animation = boundsAnimation.copy() as! CABasicAnimation
    animation.keyPath = "cornerRadius"
    let action = Action()
    action.pendingAnimation = animation
    action.priorCornerRadius = layer.cornerRadius
    return action
    }
    }
    return super.action(for: layer, forKey: event)
    }

    在上面的代码中,如果我们被要求对 cornerRadius 采取行动,我们寻找一个 CABasicAnimationbounds.size .如果找到了,就复制,把key path改成 cornerRadius ,并将其保存在自定义 CAAction 中(类 Action ,我将在下面展示)。我们还保存了 cornerRadius 的当前值属性,因为 Core Animation 调用 actionForLayer:forKey: 之前 更新属性。

    actionForLayer:forKey:返回,Core Animation 更新了 cornerRadius层的属性。然后它通过发送 runActionForKey:object:arguments: 来运行操作. Action 的工作是安装任何合适的动画。这是 CAAction 的自定义子类,我嵌套在 CircleView :
        private class Action: NSObject, CAAction {
    var pendingAnimation: CABasicAnimation?
    var priorCornerRadius: CGFloat = 0
    public func run(forKey event: String, object anObject: Any, arguments dict: [AnyHashable : Any]?) {
    if let layer = anObject as? CALayer, let pendingAnimation = pendingAnimation {
    if pendingAnimation.isAdditive {
    pendingAnimation.fromValue = priorCornerRadius - layer.cornerRadius
    pendingAnimation.toValue = 0
    } else {
    pendingAnimation.fromValue = priorCornerRadius
    pendingAnimation.toValue = layer.cornerRadius
    }
    layer.add(pendingAnimation, forKey: "cornerRadius")
    }
    }
    }
    } // end of CircleView
    runActionForKey:object:arguments:方法设置 fromValuetoValue动画的属性,然后将动画添加到图层。有一个复杂的问题:UIKit 使用“附加”动画,因为如果您在早期动画仍在运行时在属性上启动另一个动画,它们会更好地工作。所以我们的行动会检查这一点。

    如果动画是可加的,则设置 fromValue到新旧圆角半径的差异,并设置 toValue到零。由于层的 cornerRadius属性在动画运行时已经更新,添加 fromValue在动画开始时使它看起来像旧的圆角半径,并添加 toValue动画末尾的零使它看起来像新的角半径。

    如果动画不是可加的(据我所知,如果 UIKit 创建了动画就不会发生这种情况),那么它只会设置 fromValuetoValue以明显的方式。

    为方便起见,这里是整个文件:
    import UIKit

    class CircleView: UIView {

    override func layoutSubviews() {
    super.layoutSubviews()
    updateCornerRadius()
    }

    private func updateCornerRadius() {
    layer.cornerRadius = min(bounds.width, bounds.height) / 2
    }

    override func action(for layer: CALayer, forKey event: String) -> CAAction? {
    if event == "cornerRadius" {
    if let boundsAnimation = layer.animation(forKey: "bounds.size") as? CABasicAnimation {
    let animation = boundsAnimation.copy() as! CABasicAnimation
    animation.keyPath = "cornerRadius"
    let action = Action()
    action.pendingAnimation = animation
    action.priorCornerRadius = layer.cornerRadius
    return action
    }
    }
    return super.action(for: layer, forKey: event)
    }

    private class Action: NSObject, CAAction {
    var pendingAnimation: CABasicAnimation?
    var priorCornerRadius: CGFloat = 0
    public func run(forKey event: String, object anObject: Any, arguments dict: [AnyHashable : Any]?) {
    if let layer = anObject as? CALayer, let pendingAnimation = pendingAnimation {
    if pendingAnimation.isAdditive {
    pendingAnimation.fromValue = priorCornerRadius - layer.cornerRadius
    pendingAnimation.toValue = 0
    } else {
    pendingAnimation.fromValue = priorCornerRadius
    pendingAnimation.toValue = layer.cornerRadius
    }
    layer.add(pendingAnimation, forKey: "cornerRadius")
    }
    }
    }
    } // end of CircleView

    我的回答灵感来自 this answer by Simon .

    关于ios - 使用 AutoLayout 调整圆形(圆形)UIView 的大小...如何在调整大小动画期间为cornerRadius 设置动画?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35713244/

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