How to show keyboard automatically when searchController is presented - ios

#IBAction func MapSearchController(_ sender: UIBarButtonItem) {
let searchTable = storyboard!.instantiateViewController(withIdentifier: "SearchTableViewController") as! SearchTableViewController
mapSearchController = UISearchController(searchResultsController: searchTable)
mapSearchController.searchResultsUpdater = searchTable
present(mapSearchController!, animated: true, completion: nil)
mapSearchController.searchBar.becomeFirstResponder()
self.mapSearchController.dimsBackgroundDuringPresentation = true
self.mapSearchController.searchBar.sizeToFit()
self.mapSearchController.searchBar.barTintColor = UIColor.black
self.mapSearchController.searchBar.placeholder = "חפש ברים"
self.mapSearchController.hidesNavigationBarDuringPresentation = true
mapSearchController.searchBar.delegate = self
definesPresentationContext = true
searchTable.mapView = mapView
searchTable.handleMapSearchDelegate = self
How to show keyboard automatically when searchController is presented? i tried a lot of solutions but none of them worked for me ..including becomeFirstResponder() and such... please help

Problem is Search controller is not presented when view id loaded so even after making becomeFirstResponder doesn't solve the issue. Below code will fix the issue and open the keyboard with cursor in searchbar.
override func viewDidAppear(_ animated: Bool) {
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}

First, move:
present(mapSearchController!, animated: true, completion: nil)
to the end, after you are finished settings all the properties on mapSearchController.
Then make sure you add the delegate method:
func didPresentSearchController(_ searchController: UISearchController) {
searchController.searchBar.becomeFirstResponder = true
}
If that still doesn't work, make sure this VC conforms to the search UISearchControllerDelegate.

You're setting it too early. At that moment the the viewDidLoad of the your MapSearchController instance hasn't been triggered yet. You can only becomeFirstResponder after viewDidLoad.
So what you have to do is: set the becomeFirstResponder in the viewDidAppear of MapSearchController.

Related

Whole app freezes, when opening UISearchController in NavigationBar

I'm making an app with a list of crypto-currencies. There should be an ability to make a search within those 3 filtered lists (BTC, ETH, USD). The only problem i have is with the search bar.
As soon as i press on the "magnifying glass" icon, the search bar presents, but the whole app freezes. I'm not able to press "Cancel", type in the bar, close it and move the UITableView. I don't understand the reason of such behavior.
var searchController : UISearchController!
#IBAction func searchingButton(_ sender: Any) {
searchController = UISearchController(searchResultsController: nil)
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchBar.placeholder = "All currency pairs"
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchResultsUpdater = self
definesPresentationContext = true
navigationItem.searchController = self.searchController
navigationItem.hidesSearchBarWhenScrolling = false
searchButton.isHidden = true
present(searchController, animated: true, completion: nil)
}
extension ViewController: UISearchControllerDelegate {
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
navigationItem.searchController = nil
navigationController?.view.setNeedsLayout()
navigationController?.view.layoutIfNeeded()
self.indexChange(self.segmentedControl!)
}
}
Please help me with this problem. If needed, more code will be provided.
Well, it's weird, but cleaning the project and system reboot helped.

How do I hide my Searchbar when scrolling?

My SearchController has unintended behaviour of showing up translucent on top of my scrolling content:
Ideally, I want it to scroll up with my content and the navigation bar to collapse.
Any tips on how to achieve this?
I'm creating my SearchController in ViewDidLoad like so:
let searchResultsController = SearchViewController(nibName: "SearchViewController", bundle: nil)
let searchController = UISearchController(searchResultsController: searchResultsController)
searchController.searchResultsUpdater = searchResultsController
searchController.searchBar.autocapitalizationType = .sentences
searchController.obscuresBackgroundDuringPresentation = true
searchController.searchBar.placeholder = "Search Birds"
searchController.searchBar.delegate = self
navigationItem.searchController = searchController
definesPresentationContext = true
In ViewWillAppear, I set my NavigationController:
self.navigationController!.setNavigationBarHidden(false, animated: true)
I create my Navigation controller in AppDelegate like:
public func applicationDidFinishLaunching(_ application: UIApplication) {
// Instantiate the initial controller
let initialViewController = HomeViewController(nibName: "HomeViewController", bundle: nil)
let navigationController = UINavigationController(rootViewController: initialViewController)
You can try (If you App runs on iOS 11.0 and more)
override func viewDidAppear(_ animated: Bool) {
if #available(iOS 11.0, *) {
navigationItem.hidesSearchBarWhenScrolling = true
}
}
Apple doc:
If this property is true (the default), the searchController’s search
bar will hide as the user scrolls in the top view controller’s scroll
view. If false, the search bar will remain visible and pinned
underneath the navigation bar.
You can try with following:
override func viewWillAppear(_ animated: Bool) {
if #available(iOS 11.0, *) {
navigationItem.hidesSearchBarWhenScrolling = false
}
}
override func viewDidAppear(_ animated: Bool) {
if #available(iOS 11.0, *) {
navigationItem.hidesSearchBarWhenScrolling = true
}
}
navigationItem.hidesSearchBarWhenScrolling = true
Make your controller confirm to UIScrollViewDelegate and override these methods:
extension ViewController: UIScrollViewDelegate{
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.navigationController!.setNavigationBarHidden(true, animated: true)
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
self.navigationController!.setNavigationBarHidden(false, animated: true)
}
}
This might require some changes as per the aesthetics you are looking for in the app.

Swift iOS -How to get keyboard to present itself at the same exact time a SearchController is presented?

Just for context my keyboard successfully appears so that's not the problem.
I have a searchButton as my rightBarButtonItem, when pressed it modally presents a vc that contains a SearchController. When the SearchController is presented the keyboard is also presented but the keyboard appears a second late, there's like a 1 second delay before it shows itself. Basically the vc appears on the scene and then the keyboard appears afterwards, I cannot get the keyboard to appear at the same time the SearchController is presented. I was on YouTube's and Vimeo's iOS apps and when I pressed their search button the keyboard is presented with the SearchController at the same exact time, there isn't a 1 second delay.
How can I get the keyboard to present itself at the same time the SearchController is presenting itself?
button to modally present SearchController:
#objc func searchButtonTapped() {
let searchVC = SearchController()
let nav = UINavigationController(rootViewController: searchVC)
present(nav, animated: true, completion: nil)
}
SearchController:
I've already tried adding searchController.isActive = true and searchController.searchBar.becomeFirstResponder() in DispatcQeue.main in viewWillAppear and viewDidAppear and it made no difference
class SearchController: UIViewController {
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: nil)
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.searchBar.showsCancelButton = true
searchController.searchBar.placeholder = "Search"
searchController.searchBar.returnKeyType = .search
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.sizeToFit()
searchController.searchBar.tintColor = UIColor.black
definesPresentationContext = true
navigationItem.hidesBackButton = true
navigationItem.titleView = searchController.searchBar
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
searchController.isActive = true
}
// I tried both of these searchContrller delegate methods SEPERATELY but it made no difference, there's still a 1 second delay
func presentSearchController(_ searchController: UISearchController) {
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}
func didPresentSearchController(_ searchController: UISearchController) {
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}
}
Sure there is a delay.. ones this animation is completed, then keyboard appears.
present(nav, animated: true, completion: nil)
Please try this.
It will immediately open the keyboard if you would not preset view controller with an animation but if we present view controller animation it opens keyboard after the present animation is finished.
Thanks.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}

PresentViewController does not work: view is not in the window hierarchy in TabBarController

I have created a CustomAlertView.
protocol CustomAlertViewControllerDelegate{
func retryToFetchData()
}
class CustomAlertViewController: UIViewController {
#IBOutlet weak var alertView: UIView!
var delegate: CustomAlertViewControllerDelegate!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.alertView.alpha = 0
self.alertView.frame.origin.y = self.alertView.frame.origin.y + 50
UIView.animate(withDuration: 0.4) {
self.alertView.alpha = 1.0
self.alertView.frame.origin.y = self.alertView.frame.origin.y - 50
}
}
#IBAction func retryButton(_ sender: UIButton) {
delegate?.retryToFetchData()
self.dismiss(animated: true, completion: nil)
}
}
I have created a static function to show that view. The view is just a UIViewController that will have a child View which will act as a popUP, with a transparent background.
func showErrorBox(view: UIViewController, message: String, delegate: CustomAlertViewControllerDelegate){
let customAlert = view.storyboard?.instantiateViewController(withIdentifier: "customAlertViewController") as! CustomAlertViewController
customAlert.providesPresentationContextTransitionStyle = true
customAlert.definesPresentationContext = true
customAlert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
customAlert.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
customAlert.delegate = delegate
view.present(customAlert, animated: true, completion: nil)
}
Now, I am calling this VC anywhere I needed to show a popUP with showErrorBox(view: self, message: message, delegate: self) now my issue is I need to show this popup in a ViewController which is inside of a TabBarController, when I change the view between the UITabBar, and try pressing the reload button, the app throws an error,
presentViewController does not work: view is not in the window
hierarchy
Edit:
There is a Retry button on the ErrorBox(popUp). The error happens only when the error box is loaded and I changed the view to different tab and then hit the reload button. In normal scenario like, hitting the reload button when I am in the same page works fine.
I am not sure of issue, but it has something to do with when I change the view between tabs when the error box is present.
try self.present(customAlert, animated: true, completion: nil) instead view.present(customAlert, animated: true, completion: nil). If I understand your problem, should works
Solution,
The problem was, when I changed the views in TabController the reference to the customAlertView was removed from the window hierarchy. So, As soon as I leave the TabView which was loading the CustomAlertView need to be removed.
So to solve the issue, I have added following code in CustomAlertViewController
override func viewWillDisappear(_ animated: Bool) {
super. viewWillDisappear(animated)
self.dismiss(animated: true, completion: nil)
}
Try to getting the TopMost ViewController and presenting CustomAlertViewController from it instead of "self".
refer this link to :
GetTopMostViewController

Attempting to load the view of a view controller while it is deallocating... UISearchController

I have code that creates a UISearchController' in my UIVIew'sviewDidLoad`.
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.searchBar.delegate = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
controller.hidesNavigationBarDuringPresentation = false //prevent search bar from moving
controller.searchBar.placeholder = "Search for song"
self.myTableView.tableHeaderView = controller.searchBar
return controller
})()
Right after this closure finishes, this warning appears in the console:
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UISearchController: 0x154d39700>)
I don't get what I am doing wrong. This similar question is not really my situation (At least I don't think so). What is going on?
UISearchController's view has to be removed from its superview before deallocate. (guess it is a bug)
Objective-C...
-(void)dealloc {
[searchController.view removeFromSuperview]; // It works!
}
Swift 3...
deinit {
self.searchController.view.removeFromSuperview()
}
I struggled with this issue for a couple of weeks. ^^
Solved! It was a simple fix. I changed this code
class ViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
var resultSearchController = UISearchController()
to this:
class ViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
var resultSearchController: UISearchController!
This fixes the problem.
Here is the Swift version that worked for me (similar toJJHs answer):
deinit{
if let superView = resultSearchController.view.superview
{
superView.removeFromSuperview()
}
}
class SampleClass: UITableViewController, UISearchBarDelegate {
private let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.loadViewIfNeeded() // Add this line before accessing searchController
}
}
Hacking together a few solutions I managed to get mine working by adding lines to viewDidLoad before fully setting up the UISearchController:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = self.editButtonItem()
if #available(iOS 9.0, *) {
self.resultSearchController.loadViewIfNeeded()// iOS 9
} else {
// Fallback on earlier versions
let _ = self.resultSearchController.view // iOS 8
}
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
self.tableView.reloadData()
}
In Swift2 I got the same error message due to an obvious bug:
let alertController = UIAlertController(title: "Oops",
message:"bla.", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Ok",
style: UIAlertActionStyle.Default,handler: nil))
self.presentViewController(alertController, animated: true, completion: nil)
Due to a stupid copy error from myself, I had not included the self.presentViewController line. This caused the same error.
In Swift 2.2 version that worked for me
deinit {
self.searchController?.view.removeFromSuperview()
}
I think it's helpful!
It's not a bug. It seems that you have to avoid creating ViewControllers without presenting them. So after SomeViewController() or let variable: SomeViewController you have to call something like this self.presentViewController(yourViewController ...etc). If you don't do that, you will get this warning when this view controller will be dealocated.
Mine is working like this
func initSearchControl(){
searchController = UISearchController(searchResultsController: nil)
if #available(iOS 9.0, *) {
searchController.loadViewIfNeeded()
} else {
let _ = self.searchController.view
}
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
searchController.searchBar.sizeToFit()
}
searchController.loadViewIfNeeded() solves the problem but you need to call it after initializing the searchController
Creating a search controller in viewDidLoad() and setting its search bar as the navigation item's title view doesn't create a strong reference to the search controller, which is why it's deallocated.
So instead of doing this:
override func viewDidLoad() {
super.viewDidLoad()
// Create search controller
let searchController = UISearchController(searchResultsController: nil)
// Add search bar to navigation bar
navigationItem.titleView = searchController.searchBar
// Size search bar
searchController.searchBar.sizeToFit()
}
You should do this:
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
// Create search controller
searchController = UISearchController(searchResultsController: nil)
// Add search bar to navigation bar
navigationItem.titleView = searchController.searchBar
// Size search bar
searchController.searchBar.sizeToFit()
}
I used Derek's answer, but had to change it slightly.
The answer that was provided crashed for me because the call to loadViewIfNeeded() happened before the resultSearchController was defined. (My declaration was
var resultSearchController: UISearchController!
). So I just moved it afterwards and it worked.
If I left out the call entirely, the bug remained, so I'm sure it is an essential part of the answer. I was unable to test it on iOS 8.
It seem the view is lazy loaded, if you allocated the controller and never show it, the view is not loaded. In this case, if the controller is deallocated, you will received this warning. you could show it once, or call it's loadViewIfNeed() method, or use 'let _ = controller.view' to force load the view to avoid this warning.
I'm a bit late to the party, but here's my solution:
var resultSearchController: UISearchController!
override func viewDidLoad()
{
super.viewDidLoad()
self.resultSearchController = ({
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
return searchController
})()
self.tableView.tableHeaderView = self.resultSearchController.searchBar
self.tableView.reloadData()
}
I hope it works for you.

Resources