How to animate add UISearchBar on top of UINavigationBar
If I set the displaysSearchBarInNavigationBar = YES
in viewDidLoad
, the search bar will be in navigation bar when the view show up. But I want to show search bar on top of navigation bar when I touch bar button item. It's like image below
normal navigation bar:
search bar on top of navigation bar after right bar button item clicked
I've modified Mark's answer a little to get it to work in IOS 8 and in swift.
class ViewController : UIViewController, UISearchBarDelegate {
var searchBar = UISearchBar()
var searchBarButtonItem: UIBarButtonItem?
var logoImageView : UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Can replace logoImageView for titleLabel of navbar
let logoImage = UIImage(named: "logo-navbar")!
logoImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: logoImage.size.width, height: logoImage.size.height))
logoImageView.image = logoImage
navigationItem.titleView = logoImageView
searchBar.delegate = self
searchBar.searchBarStyle = UISearchBarStyle.Minimal
searchBarButtonItem = navigationItem.rightBarButtonItem
}
@IBAction func searchButtonPressed(sender: AnyObject) {
showSearchBar()
}
func showSearchBar() {
searchBar.alpha = 0
navigationItem.titleView = searchBar
navigationItem.setLeftBarButtonItem(nil, animated: true)
UIView.animateWithDuration(0.5, animations: {
self.searchBar.alpha = 1
}, completion: { finished in
self.searchBar.becomeFirstResponder()
})
}
func hideSearchBar() {
navigationItem.setLeftBarButtonItem(searchBarButtonItem, animated: true)
logoImageView.alpha = 0
UIView.animateWithDuration(0.3, animations: {
self.navigationItem.titleView = self.logoImageView
self.logoImageView.alpha = 1
}, completion: { finished in
})
}
//MARK: UISearchBarDelegate
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
hideSearchBar()
}
}
I think the basic idea would be to animate a fade-out of your existing navigation bar's items (leftBarButtonItem(s), titleView, rightBarButtonItem(s)), followed by an animated fade-in of your search bar after it is added as your navigationItem's title view. To revert, animate a fade-out of the search bar, followed by a replacement of your navigationBar's prior items.
The searchBar in the rough example below is stand-alone, but it could also come from elsewhere, like iOS8's new UISearchController. It also assumes that the view controller is embedded in a UINavigationController.
This example builds the UI programmatically, but you should be able to incorporate this approach with a Storyboard-built UI.
The animation that occurs when the user taps the "Cancel" button is a little rough, but hopefully might point the way to a smoother solution.
@interface ViewController() <UISearchBarDelegate>
@property (nonatomic, strong) UIButton *searchButton;
@property (nonatomic, strong) UIBarButtonItem *searchItem;
@property (nonatomic, strong) UISearchBar *searchBar;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// create the magnifying glass button
self.searchButton = [[UIButton alloc] init];
// add button images, etc.
[_searchButton addTarget:self action:@selector(searchButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
self.searchItem = [[UIBarButtonItem alloc] initWithCustomView:_searchButton];
self.navigationItem.rightBarButtonItem = _searchItem;
self.searchBar = [[UISearchBar alloc] init];
_searchBar.showsCancelButton = YES;
_searchBar.delegate = self;
}
- (void)searchButtonTapped:(id)sender {
[UIView animateWithDuration:0.5 animations:^{
_searchButton.alpha = 0.0f;
} completion:^(BOOL finished) {
// remove the search button
self.navigationItem.rightBarButtonItem = nil;
// add the search bar (which will start out hidden).
self.navigationItem.titleView = _searchBar;
_searchBar.alpha = 0.0;
[UIView animateWithDuration:0.5
animations:^{
_searchBar.alpha = 1.0;
} completion:^(BOOL finished) {
[_searchBar becomeFirstResponder];
}];
}];
}
#pragma mark UISearchBarDelegate methods
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[UIView animateWithDuration:0.5f animations:^{
_searchBar.alpha = 0.0;
} completion:^(BOOL finished) {
self.navigationItem.titleView = nil;
self.navigationItem.rightBarButtonItem = _searchItem;
_searchButton.alpha = 0.0; // set this *after* adding it back
[UIView animateWithDuration:0.5f animations:^ {
_searchButton.alpha = 1.0;
}];
}];
}// called when cancel button pressed
I have just refactored Nick's answer to make it the POP way in Swift 4.
protocol SearchViewAnimateble : class{ }
extension SearchViewAnimateble where Self: UIViewController{
func showSearchBar(searchBar : UISearchBar) {
searchBar.alpha = 0
navigationItem.titleView = searchBar
navigationItem.setRightBarButton(nil, animated: true)
UIView.animate(withDuration: 0.5, animations: {
searchBar.alpha = 1
}, completion: { finished in
searchBar.becomeFirstResponder()
})
}
func hideSearchBar( searchBarButtonItem : UIBarButtonItem, titleView : UIView) {
navigationItem.setRightBarButton(searchBarButtonItem, animated: true)
titleView.alpha = 0
UIView.animate(withDuration: 0.3, animations: {
self.navigationItem.titleView = titleView
titleView.alpha = 1
}, completion: { finished in
})
}
}
Then you can use it like this
class ViewController : UIViewController, UISearchBarDelegate, SearchViewAnimateble {
var searchBar = UISearchBar()
var searchBarButtonItem: UIBarButtonItem?
var logoImageView : UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Can replace logoImageView for titleLabel of navbar
let logoImage = UIImage(named: "logo-navbar")!
logoImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: logoImage.size.width, height: logoImage.size.height))
logoImageView.image = logoImage
navigationItem.titleView = logoImageView
searchBar.delegate = self
searchBar.searchBarStyle = .minimal
searchBar.showsCancelButton = true
searchBarButtonItem = navigationItem.rightBarButtonItem
}
@IBAction func searchButtonPressed(sender: AnyObject) {
showSearchBar(searchBar: searchBar)
}
//MARK: UISearchBarDelegate
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
hideSearchBar( searchBarButtonItem : searchBarButton!, titleView : logoImageView)
}
}
Following Nick's answer, I made a similar one on Xcode 7.1 -swift 2.0.
Note:
To the Navigation Bar, I added
(a) UIBarButtons( Drag& Drop) - menuButton & searchButton
(b) UIBarButtons (programatically) - leftSearchBarButtonItem & rightSearchBarButtonItem.
The common methods are :
(a) showSearchBar(), hideSearchBar()
(b) revealToggle: - It is connected to SWRevealController for Slider Menu.
// DashBoardViewController.swift
import UIKit
class DashBoardViewController: UIViewController,UISearchBarDelegate,SWRevealViewControllerDelegate {
//MARK:- STORYBOARD REFERENCE
@IBOutlet weak var menuButton: UIBarButtonItem!
@IBOutlet weak var searchButtton: UIBarButtonItem!
//Making secondary Searchbar
var searchBar = UISearchBar()
var leftSearchBarButtonItem: UIBarButtonItem?
var rightSearchBarButtonItem: UIBarButtonItem?
var logoImageView : UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
self.activateInitialUISetUp()
// self.revealViewController().delegate = self
makeTopNavigationSearchbar()
}
override func viewWillAppear(animated: Bool) {
makeTopNavigationSearchbar()
activateInitialUISetUp()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//MARK:- SEARCHBAR METHODS
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
hideSearchBar()
searchBar.resignFirstResponder()
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
hideSearchBar()
}
//Search Bar Appear & Disappear
func showSearchBar() {
searchBar.hidden = false
searchBar.alpha = 0
navigationItem.titleView = searchBar
navigationItem.setLeftBarButtonItem(nil, animated: true)
navigationItem.setRightBarButtonItem(nil, animated: true)
UIView.animateWithDuration(0.5, animations: {
self.searchBar.alpha = 1
}, completion: { finished in
self.searchBar.becomeFirstResponder()
})
}
func hideSearchBar() {
hideSearchBarAndMakeUIChanges()
logoImageView.alpha = 0
UIView.animateWithDuration(0.3, animations: {
self.logoImageView.alpha = 1
}, completion: { finished in
})
}
//Making secondary Searchbar
func makeTopNavigationSearchbar()
{
let logoImage = UIImage(named: "password")!
logoImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: logoImage.size.width, height: logoImage.size.height))
logoImageView.image = logoImage
searchButtton.customView?.addSubview(logoImageView)
searchBar.delegate = self
searchBar.searchBarStyle = UISearchBarStyle.Minimal
leftSearchBarButtonItem = navigationItem.leftBarButtonItem
rightSearchBarButtonItem = navigationItem.rightBarButtonItem
leftSearchBarButtonItem?.tintColor = UIColor.whiteColor()
rightSearchBarButtonItem?.tintColor = UIColor.whiteColor()
}
//Adding secondary uibar butttons to navigation bar
func hideSearchBarAndMakeUIChanges ()
{
searchBar.hidden = true
//Adding secondary uibarbuttons to the nav bar and revoke its methods
navigationItem.setLeftBarButtonItem(leftSearchBarButtonItem, animated: true)
navigationItem.setRightBarButtonItem(rightSearchBarButtonItem, animated: true)
leftSearchBarButtonItem?.title = "Menu"
leftSearchBarButtonItem?.target = self.revealViewController()
leftSearchBarButtonItem?.action = "revealToggle:"
rightSearchBarButtonItem?.title = "Search"
rightSearchBarButtonItem?.target = self
rightSearchBarButtonItem?.action = "showSearchBar"
//Adding Title Label
var navigationTitlelabel = UILabel(frame: CGRectMake(0, 0, 200, 21))
navigationTitlelabel.center = CGPointMake(160, 284)
navigationTitlelabel.textAlignment = NSTextAlignment.Center
navigationTitlelabel.textColor = UIColor.whiteColor()
navigationTitlelabel.text = "WORK ORDER"
self.navigationController!.navigationBar.topItem!.titleView = navigationTitlelabel
}
//UI-Related Methods
func activateInitialUISetUp()
{
self.navigationController?.navigationBarHidden = false
self.navigationController?.navigationBar.barStyle = UIBarStyle.BlackOpaque
self.navigationController?.navigationBar.translucent = true
self.navigationController?.navigationBar.backgroundColor = UIColor.redColor()
//Nav Bar Searchbar
searchBar.delegate = self
searchBar.placeholder = "Start Your Search Here"
searchButtton.action = "showSearchBar"
searchButtton.target = self
//searchbar Text Color
var textFieldInsideSearchBar = searchBar.valueForKey("searchField") as? UITextField
textFieldInsideSearchBar?.textColor = UIColor.whiteColor()
//Nav Bar Title
self.title = "WORK ORDER"
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
self.revealViewController().rearViewRevealWidth = self.view.frame.width / 2
self.revealViewController().rearViewRevealOverdraw = 0.0
self.view.addGestureRecognizer(self.revealViewController().tapGestureRecognizer())
}
}
func revealController(revealController: SWRevealViewController!, didMoveToPosition position: FrontViewPosition) {
if(position.rawValue == 3)
{
}
else
{
}
print("position\(position)")
}
}