gpt4 book ai didi

ios - UINavigationBar 彩色动画与推送动画同步

转载 作者:可可西里 更新时间:2023-11-01 00:56:00 24 4
gpt4 key购买 nike

我想在具有不同 UINavigationBar 背景颜色的 View 之间实现流畅的动画。嵌入式 View 具有与 UINavigationBar 相同的背景颜色,我想模仿推/弹出过渡动画,如:

enter image description here

我已经准备好自定义过渡:

class CustomTransition: NSObject, UIViewControllerAnimatedTransitioning {

private let duration: TimeInterval
private let isPresenting: Bool

init(duration: TimeInterval = 1.0, isPresenting: Bool) {
self.duration = duration
self.isPresenting = isPresenting
}

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let container = transitionContext.containerView
guard
let toVC = transitionContext.viewController(forKey: .to),
let fromVC = transitionContext.viewController(forKey: .from),
let toView = transitionContext.view(forKey: .to),
let fromView = transitionContext.view(forKey: .from)
else {
return
}

let rightTranslation = CGAffineTransform(translationX: container.frame.width, y: 0)
let leftTranslation = CGAffineTransform(translationX: -container.frame.width, y: 0)

toView.transform = isPresenting ? rightTranslation : leftTranslation

container.addSubview(toView)
container.addSubview(fromView)

fromVC.navigationController?.navigationBar.backgroundColor = .clear
fromVC.navigationController?.navigationBar.setBackgroundImage(UIImage.fromColor(color: .clear), for: .default)

UIView.animate(
withDuration: self.duration,
animations: {
fromVC.view.transform = self.isPresenting ? leftTranslation :rightTranslation
toVC.view.transform = .identity
},
completion: { _ in
fromView.transform = .identity
toVC.navigationController?.navigationBar.setBackgroundImage(
UIImage.fromColor(color: self.isPresenting ? .yellow : .lightGray),
for: .default
)
transitionContext.completeTransition(true)
}
)
}
}

并在 UINavigationControllerDelegate 方法实现中返回:

func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return CustomTransition(isPresenting: operation == .push)
}

虽然推送动画效果很好,但弹出动画效果不佳。

enter image description here

问题:

  1. 为什么在弹出动画之前清除 NavBar 颜色后它仍然是黄色?
  2. 有没有更好的方法来实现我的目标? (导航栏不能一直透明,因为它只是流程的一部分)

这是 link到我在 GitHub 上的测试项目。

编辑

这里是展示所讨论问题的全貌和预期效果的 gif:

enter image description here

最佳答案

这些组件总是很难定制。我认为,Apple 希望系统组件在每个应用程序中的外观和行为都相同,因为它允许在整个 iOS 环境中保持共享的用户体验。

有时,从头开始实现自己的组件比尝试自定义系统组件更容易。定制通常会很棘手,因为您不确定内部组件是如何设计的。因此,您必须处理大量边缘情况并应对不必要的副作用。

不过,我相信我有适合您情况的解决方案。我已经 fork 了您的项目并实现了您描述的行为。您可以在 GitHub 上找到我的实现.请参阅 animation-implementation 分支。


UINavigationBar

弹出动画无法正常工作的根本原因是 UINavigationBar 有它自己的内部动画逻辑。当 UINavigationController 的 堆栈发生变化时,UINavigationController 告诉 UINavigationBar 更改 UINavigationItems。因此,首先,我们需要为 UINavigationItems 禁用系统动画。这可以通过子类化 UINavigationBar 来完成:

class CustomNavigationBar: UINavigationBar {
override func pushItem(_ item: UINavigationItem, animated: Bool) {
return super.pushItem(item, animated: false)
}

override func popItem(animated: Bool) -> UINavigationItem? {
return super.popItem(animated: false)
}
}

然后 UINavigationController 应该用 CustomNavigationBar 初始化:

let nc = UINavigationController(navigationBarClass: CustomNavigationBar.self, toolbarClass: nil)

UINavigationController


由于需要在 UINavigationBar 和呈现的 UIViewController 之间保持动画流畅和同步,我们需要为 UINavigationController 创建自定义过渡动画对象并使用 CoreAnimationCATransaction

自定义转场

您对过渡动画师的实现近乎完美,但从我的角度来看,几乎没有遗漏任何细节。在文章中Customizing the Transition Animations你可以找到更多信息。另外,请注意 UIViewControllerContextTransitioning 协议(protocol)中的方法注释。

所以,我的推送动画版本如下所示:

func animatePush(_ transitionContext: UIViewControllerContextTransitioning) {
let container = transitionContext.containerView

guard let toVC = transitionContext.viewController(forKey: .to),
let toView = transitionContext.view(forKey: .to) else {
return
}

let toViewFinalFrame = transitionContext.finalFrame(for: toVC)
toView.frame = toViewFinalFrame
container.addSubview(toView)

let viewTransition = CABasicAnimation(keyPath: "transform")
viewTransition.duration = CFTimeInterval(self.duration)
viewTransition.fromValue = CATransform3DTranslate(toView.layer.transform, container.layer.bounds.width, 0, 0)
viewTransition.toValue = CATransform3DIdentity

CATransaction.begin()
CATransaction.setAnimationDuration(CFTimeInterval(self.duration))
CATransaction.setCompletionBlock = {
let cancelled = transitionContext.transitionWasCancelled
if cancelled {
toView.removeFromSuperview()
}
transitionContext.completeTransition(cancelled == false)
}
toView.layer.add(viewTransition, forKey: nil)
CATransaction.commit()
}

Pop动画的实现也差不多。 fromValuetoValue 属性的 CABasicAnimation 值的唯一区别。

UINavigationBar动画

为了使 UINavigationBar 具有动画效果,我们必须在 UINavigationBar 层上添加 CATransition 动画:

let transition = CATransition()
transition.duration = CFTimeInterval(self.duration)
transition.type = kCATransitionPush
transition.subtype = self.isPresenting ? kCATransitionFromRight : kCATransitionFromLeft
toVC.navigationController?.navigationBar.layer.add(transition, forKey: nil)

上面的代码将为整个 UINavigationBar 设置动画。为了仅对 UINavigationBar 的背景进行动画处理,我们需要从 UINavigationBar 中检索背景 View 。诀窍在于:UINavigationBar 的第一个 subview 是 _UIBarBackground View (可以使用 Xcode Debug View Hierarchy 对其进行探索)。在我们的例子中,确切的类并不重要,它是 UIView 的继承者就足够了。最后,我们可以直接在 _UIBarBackground 的 View 层上添加我们的动画过渡:

let backgroundView = toVC.navigationController?.navigationBar.subviews[0]
backgroundView?.layer.add(transition, forKey: nil)

我想指出,我们正在预测第一个 subview 是背景 View 。将来可能会更改 View 层次结构,请记住这一点。

将两个动画添加到一个 CATransaction 中很重要,因为在这种情况下这些动画将同时运行。

您可以在每个 View Controller 的 viewWillAppear 方法中设置 UINavigationBar 背景颜色。

这是最终动画的样子:

enter image description here

希望对您有所帮助。

关于ios - UINavigationBar 彩色动画与推送动画同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48291897/

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