Whole app freezes, when opening UISearchController in NavigationBar - ios

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.

Related

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()
}
}

iOS - SearchBar jumping to top as i tap on it to enter text

override func viewDidLoad() {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
self.tableView?.tableHeaderView = searchController.searchBar
}
This is default behavior when you add the search bar to tableview header.

UISearchController incorrect behavior when it is present modally

I showed UIViewController like modal style. UIViewController has UITableView. I added UISearchController to UITableView but UISearchController has incorrect behavior when it disappeared. I made many times UISearchControllers but I didn't face the this behavior.
My code
So I show the UIViewController
#objc fileprivate func selectInterlocutor(_ button: UIButton) {
let selectInterlocutorTVC = SelectInterlocutorTableViewController()
selectInterlocutorTVC.modalPresentationStyle = .overCurrentContext
selectInterlocutorTVC.modalTransitionStyle = .coverVertical
selectInterlocutorTVC.providesPresentationContextTransitionStyle = true
present(selectInterlocutorTVC, animated: true) {
}
}
I add UISearchController to tableView
fileprivate func addSearchController() {
searchController.searchBar.barStyle = .default
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.searchBar.autocapitalizationType = .words
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.sizeToFit()
tableView.tableHeaderView = searchController.searchBar
}
Please look at this video that you can see this behavior.
https://www.dropbox.com/s/l3m3q8wmqoy3qv2/SearchBarBug.mov?dl=0
How can I fix it?
I removed UISearchController and I used UISearchBar and it works for me.
fileprivate func addSearchController() {
searchBar.barStyle = .default
searchBar.delegate = self
searchBar.autocapitalizationType = .words
searchBar.showsCancelButton = true
searchBar.sizeToFit()
tableView.tableHeaderView = searchBar
}

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.

Display searchController on buttonClick

i'm trying to show a searchController on buttonClick. However it seem to create an error when the button is clicked. I get an error on the presentViewController row. What am i doing wrong in order to achieve this?
by the way i'm having this code in a tableViewController
func searchButtonClicked(sender: UIBarButtonItem) {
searchController = UISearchController(searchResultsController: self)
// searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = false
self.definesPresentationContext = true
self.presentViewController(self.searchController, animated: true, completion: nil)
}

Resources