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

时间:2019-04-30 10:47:07

标签: swift uitableview animation uinavigationcontroller uinavigationbar

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

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

我已经尝试了多种代码变体,主要是根据Ashley Mills answer和其他基于他的回答的变体,但从本质上讲,我似乎无法使动画工作!

使用Xcode 10.2在Swift 5中编写注释。

我正在使用主详细信息UITableViewController设置来管理列表和该列表中项目的详细信息。

我正在使用大标题...

navigationController?.navigationBar.prefersLargeTitles = true

...和搜索控制器...

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

...全部在viewDidLoad()方法中为“主视图控制器”设置。

这就是我所做的。

在我的项目中:

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

    在主视图控制器中:

  2. import QuartzCore;

    在详细视图控制器中:

  3. viewDidAppear(_ animated: Bool)方法中,编写CATransition代码,类似于上面提到的OP的答案(更改大标题,但不显示动画)。

    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>>
    }
    
  4. 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")
    }
    
  5. 我也尝试过添加CAMediaTimingFunction ...

        animationTransition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    
  6. 我还尝试了致电setNeedsLayoutsetNeedsDisplay ...

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

为清楚起见:

  • 标题文本确实改变了,但是没有动画;
  • 代码是用viewDidAppear(_ animated:)方法编写的,因为我希望用户看到过渡;
  • 测试持续时间很长(2秒)-如果/当动画正在播放时,我可以减少此时间;
  • 我使用情节提要中的检查器通读了所有UITableView / ControllerUINavigationController设置,以查看是否可能错过了某些内容。

有什么建议吗?

1 个答案:

答案 0 :(得分:0)

所以我已经解决了。

我必须在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

GIF of iOS Simulator illustrating CATransition push fromRight

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

SO问答How to set multi line Large title in navigation bar? ( New feature of iOS 11) 帮助我确定了下面详细介绍的过程,因此特别要感谢原始帖子和@Krunal的answer

使用相同的代码在UINavigationBar的子视图中循环(如上所述),我使用打印到终端来标识各种UINavigationItem和它们的子视图。

        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

我实际上已经两次应用了动画-在UILabel内的layer _UINavigationBarLargeTitleViewUILabel内的layer _UINavigationBarContentView上应用了动画。不过,这似乎无关紧要,因为当大标题第一次出现时,内容视图中的标签(当大标题从屏幕上滚动时,我认为它是导航栏中“旧样式”标题的标签) {1}}。

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

viewDidAppear

但是,我还没有弄清楚如何为大标题框架的大小增加动画效果,因此立即更改为两行或更多行会导致IMHO破坏标题更改的动画。

尚未在设备上进行测试,但对于iPhone和iPad sims似乎都可以正常工作。

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