2
votes

how can we get this navigation bar transition effects ?? (same as apple's Music App)

IMAGE1

in this image we have a navigation bar which is totally transparent only the navigation bar buttons are visible
image 1

IMAGE2

as you scroll up bar gets blurred same when you scroll down bar gets less blurred IMAGE2

IMAGE3

and after a certain point navigation bar becomes The default navigation bar with Title in it IMAGE3

anybody can guide me how to achieve this above mentioned Transitions Effect

what i have right now is

    func addBlurEffect() {
    // Add blur view
    var bounds = self.navigationController?.navigationBar.bounds as CGRect!
    visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .Dark))
    bounds.offsetInPlace(dx: 0.0, dy: -20.0)
    bounds.size.height = bounds.height + 20.0
    visualEffectView.frame = bounds
    visualEffectView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
    self.navigationController?.navigationBar.addSubview(visualEffectView)


    self.navigationController?.navigationBar.sendSubviewToBack(visualEffectView)

 }

am able to make blur my bar but i want it to blur it as Music app in iOS

1
It had to be Justin Beaver :-)Nicolas Miari

1 Answers

4
votes

The key is to use the scroll offset to set the alpha for the visualEffectView. Then when the image is the height of navigationBar from being completely off the screen you switch from the transparent nav bar to the normal translucent one. I created a view controller class that can do this using a UIScrollView, but the same principle applies to UITableView or UICollectionView.

import UIKit

class NavigationBlurViewController: UIViewController, UIScrollViewDelegate {

    // Might not want to hard code the height of the navBar but YOLO
    let navBarHeight: CGFloat = 66.0

    lazy var scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        return scrollView
    }()

    let contentView = UIView()

    let imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.image = UIImage(named: "Swift")
        imageView.clipsToBounds = true
        imageView.contentMode = .ScaleAspectFill
        return imageView
    }()

    lazy var visualEffectView: UIVisualEffectView = {
        let blurEffect = UIBlurEffect(style: .Light)
        let visualEffectView = UIVisualEffectView(effect: blurEffect)
        visualEffectView.translatesAutoresizingMaskIntoConstraints = false
        visualEffectView.alpha = 0.0
        return visualEffectView
    }()

    override func viewDidLoad()
    {
        super.viewDidLoad()
        self.view.addSubview(self.scrollView)
        self.scrollView.addSubview(self.contentView)
        self.contentView.addSubview(self.imageView)
        self.contentView.addSubview(self.visualEffectView)

        self.navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
        self.navigationController?.navigationBar.shadowImage = UIImage()
        self.navigationController?.navigationBar.tintColor = UIColor.blackColor()

    }

    override func updateViewConstraints()
    {
        super.updateViewConstraints()
        self.scrollView.topAnchor.constraintEqualToAnchor(self.view.topAnchor).active = true
        self.scrollView.leadingAnchor.constraintEqualToAnchor(self.view.leadingAnchor).active = true
        self.scrollView.trailingAnchor.constraintEqualToAnchor(self.view.trailingAnchor).active = true
        self.scrollView.bottomAnchor.constraintEqualToAnchor(self.view.bottomAnchor).active = true

        self.imageView.topAnchor.constraintEqualToAnchor(self.contentView.topAnchor, constant: -navBarHeight).active = true
        self.imageView.leadingAnchor.constraintEqualToAnchor(self.contentView.leadingAnchor).active = true
        self.imageView.trailingAnchor.constraintEqualToAnchor(self.contentView.trailingAnchor).active = true
        // 150.0 or however tall you want your image
        self.imageView.heightAnchor.constraintEqualToConstant(150.0 + navBarHeight).active = true

        self.visualEffectView.centerXAnchor.constraintEqualToAnchor(self.imageView.centerXAnchor).active = true
        self.visualEffectView.centerYAnchor.constraintEqualToAnchor(self.imageView.centerYAnchor).active = true
        self.visualEffectView.widthAnchor.constraintEqualToAnchor(self.imageView.widthAnchor).active = true
        self.visualEffectView.heightAnchor.constraintEqualToAnchor(self.imageView.heightAnchor).active = true
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        scrollView.delegate = self
    }

    override func viewDidLayoutSubviews()
    {
        super.viewDidLayoutSubviews()

        // Height just 1000 for example
        self.scrollView.contentSize = CGSize(width: self.view.bounds.width, height: 1000.0)
        self.contentView.frame = CGRect(x: 0.0, y: 0.0, width: self.scrollView.contentSize.width, height: self.scrollView.contentSize.height)

    }

    func scrollViewDidScroll(scrollView: UIScrollView)
    {
        // Decrease size of denominator to make it blur faster
        self.visualEffectView.alpha = scrollView.contentOffset.y * 1.0 / (self.imageView.frame.height - (2.0 * navBarHeight))

        if scrollView.contentOffset.y > (self.imageView.frame.height - (2.0 * navBarHeight)) && self.navigationController?.navigationBar.backgroundImageForBarMetrics(UIBarMetrics.Default) != nil
        {
            self.navigationController?.navigationBar.setBackgroundImage(nil, forBarMetrics: UIBarMetrics.Default)
            self.navigationController?.navigationBar.shadowImage = nil
        }
        else if scrollView.contentOffset.y < (self.imageView.frame.height - (2.0 * navBarHeight)) && self.navigationController?.navigationBar.backgroundImageForBarMetrics(UIBarMetrics.Default) == nil
        {
            self.navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
            self.navigationController?.navigationBar.shadowImage = UIImage()
        }
    }
}

Most of the effect logic is in scrollViewDidScroll. You start with the effect view over the image view but completely transparent as the y offset increases you increase the opacity and vice verse. Once you get to a point were only the height of the navigation bar is left for the image switch to the UINavigationBar's default background otherwise use UIImage() to make it transparent.

The result is:

enter image description here

enter image description here

Or a gif here

Obviously, Apple Music does other image manipulation to get soft vignettes, and you'll probably need to play around with the values to get it the way that you want it but this is should get you most of the way there.