在导航控制器中对标题文本进行动画更改

Posted

技术标签:

【中文标题】在导航控制器中对标题文本进行动画更改【英文标题】:Animate change to title text in Navigation Controller 【发布时间】:2019-04-30 10:47:06 【问题描述】:

我正在尝试对嵌入在 UINavigationController 中的详细信息 UITableViewController 的标题进行动画更改。

这两个关于堆栈溢出的问答似乎最相关......

Animate nav bar title text changeJordan H 的 OP; Transition Navigation Bar Title,雅各布的 OP

我尝试了许多代码变体,主要关注 Ashley Mills answer 和其他基于他的回答,但基本上我似乎无法使动画工作!

使用 Xcode 10.2 在 Swift 5 中编写笔记。

我正在使用 Master-Detail UITableViewController 设置来管理列表以及该列表中项目的详细信息。

我正在使用大标题...

navigationController?.navigationBar.prefersLargeTitles = true

...还有一个搜索控制器...

navigationItem.searchController = <<mySearchController>>
navigationItem.hidesSearchBarWhenScrolling = false
definesPresentationContext = true

...所有设置在主视图控制器的 viewDidLoad() 方法中。

这就是我所做的。

在我的项目中:

    导入 QuartzCore 框架,根据 Jack Bellis 的评论“您需要通过 [project]>[target]>Build Phases>Link Binaries>QuartzCore.framework 添加 QuartzCore 框架。”;

    在主视图控制器中:

    import QuartzCore;

    在细节视图控制器中:

    viewDidAppear(_ animated: Bool) 方法中,编写类似于上述OP 答案的CATransition 代码(更改大标题,但没有动画)。

    override func viewDidAppear(_ animated: Bool) 
    
        super.viewDidAppear(animated)
    
        let animationTransition = CATransition()
    
        animationTransition.duration = 2.0
    
        animationTransition.type = CATransitionType.push
        animationTransition.subtype = CATransitionSubtype.fromTop
    
        navigationController!.navigationBar.layer.add(animationTransition, forKey: "pushText")
    
        navigationItem.title = <<NEW TITLE TEXT>>
    
    

    viewDidAppear(_ animated: Bool) 方法中,编写上述 OP 答案中建议的 CATransition 代码的替代方案(仅在导航栏的中心添加一个单独的标题,不会更改大标题)。

    override func viewDidAppear(_ animated: Bool) 
    
        super.viewDidAppear(animated)
    
        let animationTransition = CATransition()
    
        animationTransition.duration = 2.0
    
        animationTransition.type = CATransitionType.fade
    
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 44))
        label.text = <<NEW TITLE TEXT>>
        navigationItem.titleView = label
    
        navigationItem.titleView!.layer.add(animationTransition, forKey: "fadeText")
    
    

    我也试过包括CAMediaTimingFunction...

        animationTransition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    

    我也试过打电话给setNeedsLayoutsetNeedsDisplay...

        navigationItem.titleView?.setNeedsLayout()
        navigationController?.navigationBar.setNeedsDisplay()
    

为了清楚起见:

标题文本确实发生了变化,但没有动画; 代码写在viewDidAppear(_ animated:)方法中,因为我想让用户看到过渡; 测试的持续时间很长(2 秒) - 如果/当动画工作时,我可能会减少此时间; 使用情节提要中的检查器,我已经阅读了所有 UITableView/ControllerUINavigationController 设置,看看我是否遗漏了什么。

有什么建议吗?

【问题讨论】:

【参考方案1】:

所以我已经解决了。

我必须在 UINavigationBar 的子视图中找到包含标题文本的特定层,然后为该层设置动画。结果正是我想要的。

这是我的答案(Swift 5 ios 12)...

override func viewDidAppear(_ animated: Bool) 

    super.viewDidAppear(animated)

    if (newTitle ?? "").isEmpty == false  // only proceed with a valid value for newTitle.

        // CATransition code
        let titleAnimation = CATransition()
        titleAnimation.duration = 0.5
        titleAnimation.type = CATransitionType.push
        titleAnimation.subtype = CATransitionSubtype.fromRight
        titleAnimation.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.easeInEaseOut)

        // this is a detail view controller, so we must grab the reference
        // to the parent view controller's navigation controller
        // then cycle through until we find the title labels.
        if let subviews = parent?.navigationController?.navigationBar.subviews 

            for navigationItem in subviews 

                for itemSubView in navigationItem.subviews 

                    if let largeLabel = itemSubView as? UILabel 

                        largeLabel.layer.add(titleAnimation, forKey: "changeTitle")
                    
                
            
        
        // finally set the title
        navigationItem.title = newTitle
   

注意:不需要import QuartzCore

...这是我确定要更改的内容的过程...

这个 SO Q&A How to set multi line Large title in navigation bar? ( New feature of iOS 11) 帮助我确定了下面详述的过程,因此特别感谢原始帖子和 @Krunal 的 answer。

使用相同的代码循环浏览UINavigationBar 的子视图(如上),我使用打印到终端来识别各种UINavigationItems 及其子视图。

        counter = 0

        if let subviews = parent?.navigationController?.navigationBar.subviews 

            for navigationItem in subviews 

                print("____\(navigationItem)")

                for itemSubView in navigationItem.subviews 

                    counter += 1

                    print("_______\(itemSubView)")
                
            
        
        print("COUNTER: \(counter)")

此代码在终端中产生以下打印(对于在模拟器中运行 iOS 12.2 的 iPhone 8 Plus)...

 ____<_UIBarBackground: 0x7f922740c000; frame = (0 -20; 414 116); userInteractionEnabled = NO; layer = <CALayer: 0x6000026ce1c0>>
 _______<UIImageView: 0x7f922740c9c0; frame = (0 116; 414 0.333333); userInteractionEnabled = NO; layer = <CALayer: 0x6000026ce7c0>>
 _______<UIVisualEffectView: 0x7f922740cbf0; frame = (0 0; 414 116); layer = <CALayer: 0x6000026ce880>>
 ____<_UINavigationBarLargeTitleView: 0x7f922740f390; frame = (0 44; 414 52); clipsToBounds = YES; layer = <CALayer: 0x6000026cd840>>
 _______<UILabel: 0x7f9227499fe0; frame = (20.1667 3.66667; 206.333 40.6667); text = 'Event Details'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000005bf250>>
 ____<_UINavigationBarContentView: 0x7f922740d660; frame = (0 0; 414 44); clipsToBounds = YES; layer = <CALayer: 0x6000026cea00>>
 _______<_UIButtonBarStackView: 0x7f92274984a0; frame = (302 0; 100 44); layer = <CALayer: 0x60000261cc60>>
 _______<_UIButtonBarButton: 0x7f922749a5c0; frame = (0 0; 82.3333 44); layer = <CALayer: 0x60000261ee60>>
 _______<UILabel: 0x7f922749a2d0; frame = (155 11.6667; 104.333 20.3333); text = 'Event Details'; alpha = 0; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000005bfc50>>
 ____<_UINavigationBarModernPromptView: 0x7f9227602db0; frame = (0 0; 0 50); alpha = 0; hidden = YES; layer = <CALayer: 0x6000026e4600>>
 COUNTER: 2

我实际上应用了两次动画 - 到 _UINavigationBarLargeTitleView 内的 UILabel layerUILabel _UINavigationBarContentView 内的 UILabel layer。然而,这似乎并不重要,因为当大标题第一次出现时,内容视图中的标签(我假设当大标题滚出屏幕时导航栏中的“旧样式”标题)隐藏在 @ 987654338@.

顺便说一句,如果您放入以下两行,您还将拥有多行大标题:

 largeLabel.numberOfLines = 0
 largeLabel.lineBreakMode = .byWordWrapping 

但是,我还没有弄清楚如何为大标题框的大小增加设置动画,所以立即更改为两行或更多行,恕我直言,破坏了标题更改的动画。

尚未在设备上进行测试,但似乎对 iPhone 和 iPad sims 都适用。

如果您发现任何错误,请告诉我,我会更新我的答案。

【讨论】:

以上是关于在导航控制器中对标题文本进行动画更改的主要内容,如果未能解决你的问题,请参考以下文章

动画导航栏标题

更改导航控制器 ios 的转换

无法更改导航项后退按钮的文本

更改导航栏控制器文本

尝试更改邮件撰写导航栏文本颜色

如何在导航控制器中将过渡动画从底部更改为顶部?