【问题标题】:UIView animate in ONLY ONE view controllerUIView 仅在一个视图控制器中进行动画处理
【发布时间】:2019-11-12 11:30:46
【问题描述】:

所以我在 swift 中使用容器视图来获得一个覆盖视图,该视图在导航控制器中的多个视图控制器之间共享。

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

这就是包含视图控制器的样子。注意背景的分层,然后是容器视图,然后是进度条视图:

此视图控制器有一个名为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 以便我可以在指定的视图控制器中设置动画而不影响当前可见的所有视图控制器吗?如果不是,解决此问题的最佳方法是什么?

【问题讨论】:

  • 为什么不用 CAShapeLayer 和 CoreAnimation 来代替?

标签: ios swift uiview core-animation


【解决方案1】:

所以,我最终接受了@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")
}

现在,不再需要我的自定义 NonAnimatingView、NonAnimatingButton 或 NonAnimatingSearchBar。 :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多