ios - UIView 仅在一个 View Controller 中设置动画

标签 ios swift uiview core-animation

所以我在 swift 中使用容器 View 来获取在导航 Controller 中的多个 View Controller 之间共享的叠加 View 。

它的工作方式是我有许多 subview 为流程创建“背景”。在这些 View 之上,我有一个容器 View ,其中嵌入了一个导航 Controller 。该导航 Controller 通过多个 View Controller 引导用户完成入职流程。在这个容器 View 之上是“进度 View ”,它的前缘与父 View 的前缘对齐,并且随着用户在引导过程中导航而增加宽度,直到最终,栏的宽度等于屏幕的宽度并且用户已经创建了他们的个人资料。 这是它的外观示例: Skip to 1:53 to see the main issue

这是包含 View Controller 的样子。注意背景的分层,然后是容器 View ,然后是进度条 View : enter image description here

这个 View Controller 有一个名为 updateProgressBar 的函数,它获取一定比例的屏幕宽度来设置动画,然后执行动画。

func updateProgressBar(to percentAsDecimal: CGFloat!) {
    UIView.animate(withDuration: 0.25) {
        self.progressBarWidth.constant = self.view.frame.width * percentAsDecimal
        self.view.layoutIfNeeded()
    }
}

问题: 因为包含的导航 Controller 在进度条的动画期间插入了一个新的 View Controller ,所以当这个新的 View Controller 布置它的 subview 时,这个布置是动画的。

我尝试过的: 我创建了自定义类,它们是 UIView 的子类,使用 UIView.performWithoutAnimation 函数来避免这种 subview 动画。

import UIKit

class NonAnimatingView: UIView {
    override func layoutSubviews() {
        UIView.performWithoutAnimation {
            super.layoutSubviews()
        }
    }
}

我用按钮做了类似的事情。

import UIKit

class NonAnimatingButton: UIButton {
    override func layoutSubviews() {
        UIView.performWithoutAnimation {
            super.layoutSubviews()
        }
    }
}

但是,当我尝试使用 UISearchBar 执行此操作时,部分动画已解析,但不是全部。

import UIKit

class NonAnimatingSearchBar: UISearchBar {
    override func layoutSubviews() {
        UIView.performWithoutAnimation {
            super.layoutSubviews()
        }
    }

    override func addSubview(_ view: UIView) {
        UIView.performWithoutAnimation {
            super.addSubview(view)
        }
    }

    override func insertSubview(_ view: UIView, at index: Int) {
        UIView.performWithoutAnimation {
            super.insertSubview(view, at: index)
        }
    }

    override func insertSubview(_ view: UIView, aboveSubview siblingSubview: UIView) {
        UIView.performWithoutAnimation {
            super.insertSubview(view, aboveSubview: siblingSubview)
        }
    }

    override func insertSubview(_ view: UIView, belowSubview siblingSubview: UIView) {
        UIView.performWithoutAnimation {
            super.insertSubview(view, belowSubview: siblingSubview)
        }
    }

    override func sendSubviewToBack(_ view: UIView) {
        UIView.performWithoutAnimation {
            super.sendSubviewToBack(view)
        }
    }

    override func bringSubviewToFront(_ view: UIView) {
        UIView.performWithoutAnimation {
            super.bringSubviewToFront(view)
        }
    }

    override func exchangeSubview(at index1: Int, withSubviewAt index2: Int) {
        UIView.performWithoutAnimation {
            super.exchangeSubview(at: index1, withSubviewAt: index2)
        }
    }
}

我还确保在推送导航 Controller View Controller 之前调用的任何布局更改都不会被动画化:

override func viewDidLoad() {
    UIView.performWithoutAnimation {
        super.viewDidLoad()
        femaleButton.backgroundColor = UIColor.lightGray.withAlphaComponent(0.25)
        maleButton.backgroundColor = UIColor.lightGray.withAlphaComponent(0.25)
        otherGenderButton.backgroundColor = UIColor.lightGray.withAlphaComponent(0.25)
        femaleButton.layer.cornerRadius = 5
        maleButton.layer.cornerRadius = 5
        otherGenderButton.layer.cornerRadius = 5
        nextButton.layer.cornerRadius = nextButton.frame.height/2
    }
}

我想知道的是: 有没有我可以调用而不是 UIView.animate 的东西,这样我就可以在指定的 View Controller 中设置动画而不影响当前可见的所有 View Controller ?如果不是,解决此问题的最佳方法是什么?

最佳答案

因此,我最终采纳了@rs7 的建议并改用了CAShapeLayer。我没有使用界面生成器,而是继续创建一个 CAShapeLayer 并将其添加到 viewDidLayoutSubviews()

var progressBar: CAShapeLayer!

override func viewDidLayoutSubviews() {

    progressBar = CAShapeLayer()
    progressBar.bounds = CGRect(x: 0, y: 0, width: 0, height: view.safeAreaInsets.top)
    progressBar.position = CGPoint(x: 0, y: 0)
    progressBar.anchorPoint = CGPoint(x: 0, y: 0)
    progressBar.backgroundColor = UIColor.secondaryColor.cgColor
    view.layer.addSublayer(progressBar)
}

然后我继续更新 updateProgressBar 以调整 CALayer 的大小,而不是 NSLayoutConstraint 常量。

func updateProgressBar(to percentAsDecimal: CGFloat!) {
    let newWidth = view.bounds.width * percentAsDecimal
    CATransaction.begin()
    CATransaction.setCompletionBlock({
        self.progressBar.bounds.size.width = newWidth
    })
    CATransaction.commit()
    let anim = CABasicAnimation(keyPath: "bounds")
    anim.isRemovedOnCompletion = false
    anim.fillMode = .forwards
    anim.duration = 0.25
    anim.fromValue = NSValue(cgRect: CGRect(x: 0, y: 0, width: progressBar.bounds.width, height: view.safeAreaInsets.top))
    anim.toValue = NSValue(cgRect: CGRect(x: 0, y: 0, width: newWidth, height: view.safeAreaInsets.top))
    progressBar.add(anim, forKey: "anim")
}

现在,不再需要我的自定义 NonAnimatingViewNonAnimatingButtonNonAnimatingSearchBar。 :)

关于ios - UIView 仅在一个 View Controller 中设置动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56841929/

相关文章:

ios - 更改 iPhone 的日期和时间

ios - 在另一个自定义警报或 uialertview 上方显示自定义警报

ios - iOS 中使用 URL 方案的应用程序间通信

ios - 将 UITabBarDelegate 设置为 UIViewController 时应用程序崩溃

ios - 创建的每个实例的随机颜色生成器和 RGB

ios - 如何使用 Autolayout 在不同设备上保持 UIView 比率?

ios - 如何播放抽奖动画然后在iOS/Swift/Xcode中逐帧循环播放?

ios - 您的二进制文件未针对 iPhone 5 xcassets 进行优化

ios - UIView 的 cornerRadius 不均匀影响 View

ios - 如何检查 UITableViewCell 是否包含 UIView