动画导航栏标题文本更改

17
在我的应用程序中,我有一个页面视图控制器,允许用户在应用程序的不同“部分”之间滑动,在导航栏顶部通过 pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted: 更改标题文本以显示用户滑动到的新部分。目前,当动画完成时立即更改标题文本。我想用一些动画来改善它,即一种微妙的淡入和淡出效果。
我首先尝试使用 [UIView animationWithDuration:...] 来动画更改标题文本,但它并没有动画效果,仅仅是瞬间更新。
然后我想知道是否可以在用户水平滚动时基于他们已经滚动了多远来更新导航栏标题的透明度,当下一个部分即将出现时达到0透明度,然后我可以在透明度为0时立即更改文本,然后快速淡入到1透明度。但我没有看到在UIPageViewControllerDelegate 上调用滚动位置已更新的方法。
如果可能,除了淡入淡出之外,我可以像从推送转场返回时默认发生的动画一样淡入淡出并移动导航栏标题文本的位置。当用户滚动时,我将旧的部分标题向一侧滑动并提供下一个部分标题,以便在完成转换时,先前的部分标题已经离开屏幕,新的部分标题完全居中,以便替换完成。但是这还需要准确知道用户滚动了多少个页面视图控制器。
是否可以实现任何所需的动画?

你尝试过为导航栏标题使用自定义视图吗?self.navigationItem.titleView = customNavTitleLabel; - Zhang
@张,我还没有,我会调查一下。有没有办法从现有的标题中获取UILabel,以便我可以获取默认的一个(启用粗体文本后会更改),将其更改并设置为调整后的一个? - Jordan H
我觉得我见过有人使用一些疯狂的递归循环来查找导航标题子视图 :P 如果你想的话可以尝试一下。 - Zhang
谢谢@张。我最终决定创建一个自定义标题,并使用页面视图控制器的隐藏scrollView的委托方法进行动画处理。现在它运行得非常好。 - Jordan H
5个回答

48

Swift 5

let fadeTextAnimation = CATransition()
fadeTextAnimation.duration = 0.5
fadeTextAnimation.type = .fade
    
navigationController?.navigationBar.layer.add(fadeTextAnimation, forKey: "fadeText")
navigationItem.title = "test 123"

为了方便起见,这是Ashley Mills在Swift中的解决方案:
11/16/2016更新至Swift 3(感谢n13)
let fadeTextAnimation = CATransition()
fadeTextAnimation.duration = 0.5
fadeTextAnimation.type = kCATransitionFade

navigationController?.navigationBar.layer.add(fadeTextAnimat‌​ion, forKey: "fadeText")
navigationItem.title = "test 123"

Swift 2.x
let fadeTextAnimation = CATransition()
fadeTextAnimation.duration = 0.5
fadeTextAnimation.type = kCATransitionFade

navigationController?.navigationBar.layer.addAnimation(fadeTextAnimation, forKey: "fadeText")
navigationItem.title = "test 123"

我向艾什莉致敬!

1
Swift 3 navigationController?.navigationBar.layer.add(fadeTextAnimation, forKey: "fadeText") Swift 3 navigationController?.navigationBar.layer.add(fadeTextAnimation, forKey: "fadeText") - n13
1
注意到关于 type 属性的文档。"转换的名称。当前合法的转换类型包括淡入、移入、推动和揭示。默认为淡入",因此如果 type 属性设置为淡入,则是不必要的。 - Oscar Apeland
1
如果我没记错的话,当与UISplitViewControllers一起使用时,它并不总是fade。随着新的iOS版本的推出,这可能已经改变了。如果我确切地知道我想要什么并需要它,我总是选择防御性编程,明确地请求它。但无论如何,感谢您提醒一个可能不必要的声明! - Frederik Winkelsdorf
@FrederikA.Winkelsdorf 旧的东西将被弃用,代码应该保持新鲜状态。 - dimpiax
@dimpiax 不知道这个答案在这个上下文中的意思是什么:将你的Swift 5代码与Swift 3代码进行比较。它们完全相同 ;) - Frederik Winkelsdorf
显示剩余3条评论

48

如果你想在不同的标题字符串之间进行动画,可以使用以下代码:

CATransition *fadeTextAnimation = [CATransition animation];
fadeTextAnimation.duration = 0.5;
fadeTextAnimation.type = kCATransitionFade;

[self.navigationController.navigationBar.layer addAnimation: fadeTextAnimation forKey: @"fadeText"];
self.navigationItem.title = "My new title";

当然,你可以调整持续时间并设置时间函数以适应需要。

还有其他类型的动画,在不同情况下可能会起作用(感谢@inorganik):

kCATransitionFade
kCATransitionMoveIn
kCATransitionPush
kCATransitionReveal

1
对于未经训练的人,需要注意的是,如果我的经验是明确的,您需要通过[项目]>[目标]>构建阶段>链接二进制文件>QuartzCore.framework添加QuartzCore框架。 - Jack Bellis

2
一种解决方法是创建自定义标题并使用页面视图控制器的隐藏scrollView的委托方法来动画其位置。正如张所述,自定义标题只需为self.navigationItem.titleView = customNavTitleLabel;

1
如果您只需要动画标题(而不是整个导航栏) 开发自定义标题控件 链接example 分配
  let titleView = UIAnimatedTitleView(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
 titleView.text = "Hello"

动画

var flag = true

@objc private func animateNavigationTitle() {
            guard let titleView = navigationItem.titleView as? UIAnimatedTitleView else { return }

        let fadeTextAnimation = CATransition()
        fadeTextAnimation.duration = 0.5
        fadeTextAnimation.type = kCATransitionPush
        fadeTextAnimation.subtype = kCATransitionFromTop

        titleView.layer.add(fadeTextAnimation, forKey: "pushText")
        titleView.text = flag ? "Hello" : "Good buy" 
        flag = !flag
    }

0
在编程领域中,找到了最好的方法是这个类别:
@implementation UIViewController (ControllerNavigationEffects)

-(void) setNavigationTitleWithAnimation:(NSString *) title {
    if ([self.navigationItem.title isEqualToString:title]) {
        return;
    }
    @weakify(self);
    float duration = 0.2;
    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        @strongify(self);
        self.navigationItem.titleView.alpha = 0;
    } completion:^(BOOL finished) {}];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        @strongify(self);
        self.navigationItem.titleView = nil;
        self.navigationItem.title = title;

        [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            self.navigationItem.titleView.alpha = 1;
        } completion:nil];
    });
}

-(void) setNavigationTitleViewWithAnimation:(UIView *) titleView {
    if ([self.navigationItem.titleView isKindOfClass:[titleView class]]) {
        return;
    }
    @weakify(self);
    float duration = 0.2;

    CATransition *fadeTextAnimation = [CATransition animation];
    fadeTextAnimation.duration = duration;
    fadeTextAnimation.type = kCATransitionFade;

    [self.navigationController.navigationBar.layer addAnimation: fadeTextAnimation forKey: @"fadeText"];
    self.navigationItem.title = @"";


    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        @strongify(self);
        self.navigationItem.title = @"";
        self.navigationItem.titleView = titleView;

        [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            self.navigationItem.titleView.alpha = 1;
        } completion:nil];

    });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接