UIView仅在一个视图控制器中进行动画处理

时间:2019-07-01 20:18:09

标签: ios swift uiview core-animation

因此,我正在迅速使用容器视图来获取覆盖视图,该视图在导航控制器中的多个视图控制器之间共享。

此方法的工作方式是,我有许多子视图为流创建“背景”。在这些视图上方,我有一个容器视图,其中嵌入了一个导航控制器。该导航控制器通过多个视图控制器引导用户完成入职过程。在该容器视图上方是“进度视图”,其前边缘与超级视图的前边缘对齐,并且随着用户在启动过程中导航直至其宽度最终等于屏幕的宽度而在宽度上有所增加。用户已经创建了他们的个人资料。 这是一个外观的示例: Skip to 1:53 to see the main issue

这是包含视图控制器的外观。请注意背景的分层,然后是容器视图,然后是进度栏视图: enter image description here

此视图控制器具有一个名为updateProgressBar的功能,该功能会使用一定比例的屏幕宽度进行动画处理,然后执行动画。

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

问题: 由于包含的导航控制器在进度条的动画过程中正在推动新的视图控制器,因此当该新视图控制器对子视图进行布局时,该布局将被动画化。

我尝试过的事情: 我创建了自定义类,这些自定义类是UIView的子类,它们使用UIView.performWithoutAnimation函数来避免对子视图进行动画处理。

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)
        }
    }
}

我还确保在按下导航控制器视图控制器之前调用的所有布局更改都不会设置动画:

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调用的,以便可以在指定的视图控制器中创建动画,而不会影响当前可见的所有视图控制器吗?如果没有,那么解决此问题的最佳方法是什么?

1 个答案:

答案 0 :(得分:0)

因此,我最终接受了@ 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。 :)