passatgt
passatgt

Reputation: 4432

Shrink Navigation Bar on scroll based on a Scroll View

I have a Scroll View set to a fixed height inside my View Controller. I want to use a navigation bar on top with large titles, so when i scroll the Scroll View, it should collapse like in a Navigation Controller. Is it possible to do this? My scene look like this:

Dashboard Scene Layout

The navigation bar has top/left/right 0 constraints agains the View. Currently it stays on top correctly, however it won't collapse on scroll as expected.

Upvotes: 1

Views: 2475

Answers (3)

Sai kumar Reddy
Sai kumar Reddy

Reputation: 1829

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        var height = CGFloat()
        if(scrollView.panGestureRecognizer.translation(in: scrollView.superview).y > 0) {
            height = 130
        }
        else {
            height = 44
        }
        UIView.animate(withDuration: 0.5) {
            self.navBarHeightConstraint?.constant = height
            self.view.layoutIfNeeded()
        }
    }

Upvotes: 0

passatgt
passatgt

Reputation: 4432

In the end i created a custom view to replicate the Navigation Bar. Here you can see how it looks and read the steps below to replicate:

enter image description here

  1. To setup your View Controller to be used with a custom Scroll View, first make sure you are using Freeform size for your controller. To do this, select Freeform in the size inspector and set the height to your new Scroll View's height:

    enter image description here

  2. Insert your Scroll View and setup 0 top/left/right/bottom constraints, so it will be the same size as your View Controller:

    enter image description here

  3. Add your content to your scroll view as usual

  4. Now to create your custom Navigation Bar, add a View outside of your Scroll View and setup constraints like this:

    enter image description here

    Notice a few things:

    • the top constraint is aligned to the Superview instead of the Safe Area, so the view goes behind the status bar
    • The height is set to >= 44, so its a minimum height and can expand if the content is larger
    • On the Attribute Inspector, select clip to bounds, so your content inside the view won't overflow(like in CSS, overflow:hidden)
    • At this point you might see some errors in your Storyboard, but don't worry about it: its because you don't have any content in your View and it doesn't know how tall it should be
  5. Set the View background to transparent and add a "Visual Effect View with Blur" inside, with 0 top/left/right/bottom constraints. This will blur the content behind the custom navigation bar

  6. Now make sure that you check the Safe Area Layout Guide checkbox in your navigation bar view(its above the constraints setup):

    enter image description here

    This way you can add content inside the view that won't be behind the status bar, because its outside of the safe area. And it works with the notch too.

  7. Add a label inside your view, set top and bottom constraints to Safe Area and make sure you have a fixed height constraint defined too:

    enter image description here

    Now you can also see that the errors in your Storyboard are gone :) At this point this is how everything should look like:

    enter image description here

  8. Now the coding part. In your ViewController, make outlets for both the ScrollView and the custom navigation bar. To do this, switch to the assistant editor(the venn-diagram symbol top right), select the element in your storyboard, hold down CTRL and drag inside your ViewController class:

    enter image description here

    Do the same for your View that is your navigation bar:

     @IBOutlet weak var mainScrollView: UIScrollView!
     @IBOutlet weak var customNavigationBar: UIView!
    
  9. Next, you need to add the UIScrollViewDelegate to your class, so you can listen to the scroll event and get the actual Y position of the current scroll offset using the scrollViewDidScroll function:

     class ViewController: UIViewController, UIScrollViewDelegate {
    

    You also need to setup the delegate in your viewDidLoad hook:

    mainScrollView.delegate = self
    
  10. Create a new function called scrollViewDidScroll to get the scroll position and you can use this to do various animations with other elements. In this case, if the scroll position reaches 44(this is the height i set for my custom navigation bar), it will animate to full opacity:

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let y = self.mainScrollView.contentOffset.y
        let barHeight = 44
    
        if(y < barHeight) {
            customNavigationBar.alpha = y/CGFloat(barHeight)
        } else {
            customNavigationBar.alpha = 1
        }
    }
    

    You can use the same logic to animate the label inside the navigation bar, change its size etc...

The full ViewController:

class ViewController: UIViewController, UIScrollViewDelegate {
    @IBOutlet weak var mainScrollView: UIScrollView!
    @IBOutlet weak var customNavigationBar: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        mainScrollView.delegate = self
        customNavigationBar.alpha = 0
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let y = self.mainScrollView.contentOffset.y
        let barHeight = 44

        if(y < 44) {
            customNavigationBar.alpha = y/CGFloat(barHeight)
        } else {
            customNavigationBar.alpha = 1
        }
    }

}

Upvotes: 1

matt
matt

Reputation: 535305

Do not use a "loose" navigation bar like this. Use a navigation controller, even if you do not intend to do any navigation. It gives you the desired behavior, for free.

Upvotes: 2

Related Questions