How do I override the default animation for the dismissal of a searchBar belonging to a UISearchController?
Okay so I am trying to create a custom animation for when the UISearchBar that is attached to a UISearchController becomes active. It seems the standard animation expects for the searchBar to begin with a width that takes up the screen. When the animation begins it shrinks the searchBar and fades in a cancel button to the right of it.
I want to have my searchBar begin in a small state, roughly half the screen width, to allow for two buttons to be placed in the navbar next to it as well.
When the searchBar becomes active I want the animation to expand the searchBar and for the cancel button to fade in.
When the searchBar is dismissed I want the exact opposite animation to occur: Cancel button fade out and searchBar to shrink to it's original size.
I have found a way to achieve the desired presenting animation by using a UISearchControllerDelegate method, presentSearchController:
func presentSearchController(searchController: UISearchController) {
// Animate Buttons
UIView.animateWithDuration(0.1, animations: {
// First Hide Buttons
self.createMoxyButton.alpha = 0
self.centerMapButton.alpha = 0
})
// Animate Search Bar
UIView.animateWithDuration(0.5, animations: {
// Search Bar
searchController.searchBar.frame = CGRectMake(searchController.searchBar.frame.origin.x, searchController.searchBar.frame.origin.y, self.wBounds - 40, searchController.searchBar.frame.height)
self
})
}
but I have not been able to achieve the dismissal animation. I have tried using the didDismissSearchController: and willDismissSearchController: delegate methods but it results in weird behavior and does not use the animation of frames that I set in these respective delegate methods. When the searchBar is dismissed it will expand to the full screen width, while fading out cancel button, then it will immediately change the frame of the searchBar back to the original size, ignoring my animation. I have also tried using the removeAllAnimation() method to try and stop the default animation from taking place, but to no avail.
func didDismissSearchController(searchController: UISearchController) {
searchController.searchBar.layer.removeAllAnimations()
// Animate
UIView.animateWithDuration(0.5, animations: {
// Show hidden buttons
self.createMoxyButton.alpha = 1
self.centerMapButton.alpha = 1
// Search Bar
searchController.searchBar.frame = CGRectMake(searchController.searchBar.frame.origin.x, searchController.searchBar.frame.origin.y, self.wBounds - 10 - self.createMoxyButton.frame.size.width - 20 - self.centerMapButton.frame.size.width - 20, searchController.searchBar.frame.height)
self
})
}
Gif Animation begins with the searchBar in the Active state with the cancel button visible
Subclass UISearchController and implement the optional UIViewControllerTransitioningDelegate
method
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
to return your animation object to do the animation for the dismiss.
I cannot claim that this produces super smooth animation, but it doesn't look terrible and might be helpful (or even exactly what you need). If you want to try it out, you can create a new project (single view application) and just add navigation controller as the initial one and object of class ViewController
as its root controller (just to quickly get the navigation bar). Code below is my whole implementation of ViewController
import UIKit
class ViewController: UIViewController,UISearchBarDelegate,UISearchControllerDelegate,UISearchResultsUpdating {
lazy var createMoxyButton:UIBarButtonItem = {
//customize this as your equire
let button = UIBarButtonItem(title: "Done", style: .Plain, target: nil, action: nil)
return button
}()
lazy var centerMapButton:UIBarButtonItem = {
//customize this as your equire
let button = UIBarButtonItem(title: "Done", style: .Plain, target: nil, action: nil)
return button
}()
lazy var searchController:UISearchController = {
let controller = UISearchController(searchResultsController: self)
controller.delegate = self
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.hidesNavigationBarDuringPresentation = false
return controller
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.titleView = self.searchController.searchBar
self.navigationItem.rightBarButtonItems = [self.centerMapButton,self.createMoxyButton]
self.edgesForExtendedLayout = UIRectEdge.None
self.searchController.searchBar.delegate = self
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
//this needs to be done because in shouldEndEditing
//we need to set it to false to smooth animation
searchBar.showsCancelButton = true
self.navigationItem.setRightBarButtonItems(nil, animated: true)
}
func searchBarShouldEndEditing(searchBar: UISearchBar) -> Bool {
searchBar.showsCancelButton = false
self.navigationItem.rightBarButtonItems = [self.centerMapButton,self.createMoxyButton]
return true
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With