10

In an app called "Luvocracy" the title of the navigation bar is changed when the user swipes up on the screen. The old title is pushed up, while the new one is transitioned in. I don't have a video of it now, but here are some screen shots:

https://www.dropbox.com/s/sns0bsxkdv7pw3l/Photo%20Apr%2008%2C%2011%2001%2005%20AM.png

https://www.dropbox.com/s/ys9a49u3dyxrlcm/Photo%20Apr%2008%2C%2011%2001%2009%20AM.png

https://www.dropbox.com/s/dlcfvfvqqov3ag7/Photo%20Apr%2008%2C%2011%2001%2013%20AM.png

How can I animate or transition in a new navigation bar title as shown?

Edit: The app is no longer available on the app store so I am unable to upload a video of this action.

Jacob
  • 2,338
  • 2
  • 22
  • 39
  • My guess is that they have a custom view in the navigation bar, in which they do the animation. – rckoenes Apr 08 '14 at 15:09
  • How would I go about doing that? – Jacob Apr 08 '14 at 15:16
  • What have you tried? It sound to like you are asking for code, please try to solve the issue your self – rckoenes Apr 08 '14 at 15:17
  • Sorry, I have not really tried anything yet. I understand how to add a custom view to the navigation bar, but how would I "push" up the new title as I scroll to the top of the view? – Jacob Apr 08 '14 at 22:24

4 Answers4

29

You can animate the title changing by using a CATransition... however, because the title itself is a private property on the navigation bar, you need to first create a custom label and attach that to the navigation item.

Setup the title label (this would override the default navigation bar's title):

UILabel *titleLabelView = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 44.0f) /* auto-sized anyway */];
titleLabelView.backgroundColor = [UIColor clearColor];
titleLabelView.textAlignment = NSTextAlignmentCenter;
titleLabelView.textColor = [UIColor blackColor];
titleLabelView.font = [UIFont systemFontOfSize:16.0f];
titleLabelView.adjustsFontSizeToFitWidth = YES;
titleLabelView.text = @"@cracy123";
self.navigationItem.titleView = titleLabelView;

Then whenever you want to animate the title changing (assume on a scroll view delegate action), add a CAAnimation layer and presto:

CATransition *animation = [CATransition animation];
animation.duration = 1.0;
animation.type = kCATransitionPush;
animation.subtype = kCATransitionFromTop;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.navigationItem.titleView.layer addAnimation:animation forKey:@"changeTitle"];

((UILabel*)self.navigationItem.titleView).text = @"JACOB K";

You can obviously change the CATransition animation properties to get the effect you're after, but those will give you the 'push-up' effect.

enter image description here

Gavin Bunney
  • 1,240
  • 1
  • 12
  • 12
  • Thank you for the quick answer! So the way I am looking to apply this is I have a tableview with a "header" above it which is my game's logo. As I scroll my tableview up, I want this image to move up and get smaller, and eventually move into the navigation bar's title view. Is this possible? – Jacob Feb 25 '15 at 03:27
  • You could in theory do that, but would be very fiddly animating the views to get them into the position (not impossible, but you'd have to move the frame 1px for each scroll even you receive, if that makes sense). You might be better off replicating a UINavigationBar style (i.e. not an actual nav bar) in the UITableView header, then you'd be able to control the tableview header height/labels much easier – Gavin Bunney Feb 25 '15 at 03:52
  • Could I make it so that the image is the table header, and when the image gets scrolled under the navigation bar it slides up into the title view like in the animation you show above? – Jacob Feb 25 '15 at 03:53
  • That could work - there a similar tableview subclasses out there in the wild that do what people call parallax (even if it isn't technically 'parallax' ;)) - that would get you 50% of the way there - https://github.com/pyro2927/ParallaxBlur – Gavin Bunney Feb 25 '15 at 04:04
  • Very interesting...thanks for those links...I think the second one might be what I am looking for if I can tweak it a little bit... – Jacob Feb 25 '15 at 04:09
  • Is there anyway I can implement this with the stock navigation bar, though? Where the image slides up like you have in your answer? I need to have bar button items as well, that's why I'd like to do it this way, it makes it easier for navigation. – Jacob Feb 25 '15 at 04:14
  • Yes you can; you would have a scrollView delegate method to track the tableview scrolling, then when you hit your threshold, start adjusting the size of the tableview header frame (to shrink), then when you reach another threshold, start making the image in the navigation bar header appear (as the example I posted is using a UILabel, you could be using a UIImageView or any UIView subclass) – Gavin Bunney Feb 25 '15 at 04:53
7

Just because a Swift answer was missing, here's the Swift implementation of Gavin's answer:

Setting up the custom title label:

let titleLabelView = UILabel.init(frame: CGRect(x: 0, y: 0, width: 0, height: 44))
titleLabelView.backgroundColor = UIColor.clearColor()
titleLabelView.textAlignment = .Center
// this next line picks up the UINavBar tint color instead of fixing it to a particular one as in Gavin's solution
titleLabelView.textColor = UINavigationBar.appearance().tintColor
titleLabelView.font = UIFont.boldSystemFontOfSize(16.0)
titleLabelView.text = "Old Title"
self.navigationItem.titleView = titleLabelView

And here's the title animation code:

let titleAnimation = CATransition()
titleAnimation.duration = 0.5
titleAnimation.type = kCATransitionPush
titleAnimation.subtype = kCATransitionFromTop
titleAnimation.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)

navigationItem.titleView!.layer.addAnimation(titleAnimation, forKey: "changeTitle")
(navigationItem.titleView as! UILabel).text = "New Title"

// I added this to autosize the title after setting new text
(navigationItem.titleView as! UILabel).sizeToFit()
Gligor
  • 2,862
  • 2
  • 33
  • 40
5

This was a great answer but it took some tweaking for me to get it right.

So the general idea is that you have a text in the middle of your scrollView and when the user scrolls up past that text then you want it to become the new title. Furthermore, when you scroll back down, you want it to change back to the default title.

enter image description here

enter image description here

So, using the code that Gix posted, now converted to Swift 3, here's how you get it done.

Add these variables to the top of your viewController

var didChangeTitle = false
let defaultTitle = "Default Title"
let animateUp: CATransition = {
    let animation = CATransition()
    animation.duration = 0.5
    animation.type = kCATransitionPush
    animation.subtype = kCATransitionFromTop
    animation.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)
    return animation
}()
let animateDown: CATransition = {
    let animation = CATransition()
    animation.duration = 0.5
    animation.type = kCATransitionPush
    animation.subtype = kCATransitionFromBottom
    animation.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)
    return animation
}()

In your viewDidLoad, add this code to set the default title.

let titleLabelView = UILabel.init(frame: CGRect(x: 0, y: 0, width: 200, height: 44))
titleLabelView.backgroundColor = .clear
titleLabelView.textAlignment = .center
titleLabelView.textColor = UINavigationBar.appearance().tintColor
titleLabelView.font = UIFont.boldSystemFont(ofSize: 16)
titleLabelView.text = defaultTitle
self.navigationItem.titleView = titleLabelView

Now you add some code to your scrollView delegate function:

extension MyViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView.contentOffset.y >= (labelName.frame.origin.y + labelName.frame.height) && !didChangeTitle {
            if let label = navigationItem.titleView as? UILabel {
                label.layer.add(animateUp, forKey: "changeTitle")
                label.text = labelName.text
            }
            didChangeTitle = true
        } else if scrollView.contentOffset.y < labelName.frame.origin.y && didChangeTitle {
            if let label = navigationItem.titleView as? UILabel {
                label.layer.add(animateDown, forKey: "changeTitle")
                label.text = defaultTitle
            }
            didChangeTitle = false
        }
    }
}

The "labelName" var is the label in your scrollview that contains your future title.

Travis M.
  • 10,930
  • 1
  • 56
  • 72
1

This is an example:

UILabel *titleLabelView = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f) /* auto-sized anyway */];
titleLabelView.backgroundColor = [UIColor clearColor];
titleLabelView.textAlignment = NSTextAlignmentCenter;

(and others applications)

self.navigationItem.titleView = LabelView;

I don't understand very well your question!

halfer
  • 19,824
  • 17
  • 99
  • 186