updateSearchResults function is not being called when searchBar is touched - ios

The updateSearchResults function isn't getting called in my view table controller view for some reason and I don't know why? I've seen this answer but it's nothing similar to mine and I don't want to use the textDidChange function.
I have this view SearchViewController where it has a search bar at the top and it will have a collection view below it. (Making instagrams explore page). Once the search bar has been tapped, it should display view SearchTableViewController which shows the results of the search query in a table view.
This is my code:
SearchViewController (the main view):
import UIKit
class SearchViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let searchResultsViewController = storyboard.instantiateViewController(withIdentifier: "SearchResultsViewController") as? SearchTableViewController else { return}
let searchController = UISearchController(searchResultsController: searchResultsViewController)
searchController.searchResultsUpdater = searchResultsViewController
view.addSubview(searchController.searchBar)
definesPresentationContext = true
}
}
SearchTableViewController (the view that gets displayed with the results):
import UIKit
import Firebase
class SearchTableViewController: UIViewController {
let languages = ["Mandarin Chinese", "English", "Hindustani", "Spanish", "Arabic", "Malay", "Russian", "Bengali", "Portuguese", "French", "Hausa", "Punjabi", "German", "Japanese", "Persian", "Swahili", "Telugu", "Javanese", "Wu Chinese", "Korean"]
var searchResults: [String] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
}
}
extension SearchTableViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
print("typing")
guard let searchText = searchController.searchBar.text else { return }
searchResults = languages.filter { $0.contains(searchText) }
tableView.reloadData()
}
}
extension SearchTableViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResults.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "searchCell", for: indexPath)
let language = searchResults[indexPath.row]
cell.textLabel?.text = language
return cell
}
}
the updateSearchResults in the extension SearchTableViewController: UISearchResultsUpdating isn't being called which I think is the cause of the table view with the search results not loading the search query data?

I forgot to add the assignment to the navigation controller too. Even if you're showing the search results on another screen you still need to add it:
navigationItem.searchController = theSearchControllerYouInstantiated

Related

Use of UISearchController causes objc_weak_error

After adding an UISearchController to a UITableViewController, the following objc_weak_error is logged every time the app goes to to the background (by hitting the home button) if the "Malloc Scribble" Diagnostics option is activated in the Run Scheme.
objc[8426]: __weak variable at 0x... holds 0x5555555555555555 instead
of 0x.... This is probably incorrect use of objc_storeWeak() and
objc_loadWeak(). Break on objc_weak_error to debug.
Setting the suggested breakpoint unfortunately does not provide helpful information.
I have faced this issue in a larger project, so I tried to reproduce it on a very simple app based on the "Single View App" template (xcode 10.1) with an UITableViewController embedded in a Navigation Controller with the following code. After performing a search, going to the background by hitting the home button will always trigger the error.
I was not able to find any useful information about this issue neither on stackoverflow nor on the web.
import UIKit
class TableViewController: UITableViewController {
let data = ["Berlin", "London", "New York", "Paris", "Tokyo"]
var filteredData = [String]()
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search Candies"
navigationItem.searchController = searchController
definesPresentationContext = true
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return isFiltering() ? filteredData.count : data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = isFiltering() ? filteredData[indexPath.row] : data[indexPath.row]
return cell
}
func searchBarIsEmpty() -> Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredData = data.filter { $0.lowercased().contains(searchText.lowercased())}
tableView.reloadData()
}
func isFiltering() -> Bool {
return searchController.isActive && !searchBarIsEmpty()
}
}
extension TableViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
Is the error a result of a wrong usage of UISearchController in the code above or is this an iOS bug?
Or can it just be ignored?

self.performSegue(withIdentifier: "teacherDetail", sender: self) needs two taps

I'm trying to segue from a search result in a tableview controller in swift 3 but when I use self.performSegue(withIdentifier: "teacherDetail", sender: self) it seems to hang when selecting a cell. If I tap the first cell it will show gray like it is selected, and then won't do anything unless I select another cell. Then, it will preform the segue to the detail view controller with the information from the first cell.
import UIKit
var name = ""
class DirectoryTC: UITableViewController, UISearchResultsUpdating {
var teachers = ["Mr. Delano", "Mr. Antani", "Mr. Botelho", "Mr. Braga"]
var filteredTeachers = [String]()
var searchController: UISearchController!
var resultsController = UITableViewController()
override func viewDidLoad() {
super.viewDidLoad()
self.resultsController.tableView.dataSource = self
self.resultsController.tableView.delegate = self
self.searchController = UISearchController(searchResultsController: self.resultsController)
self.tableView.tableHeaderView = self.searchController.searchBar
self.searchController.searchResultsUpdater = self
definesPresentationContext = true
}
func updateSearchResults(for searchController: UISearchController) {
self.filteredTeachers = self.teachers.filter{ (teacher:String) -> Bool in
if teacher.lowercased().contains(self.searchController.searchBar.text!.lowercased())
{
return true
}else
{
return false
}
}
self.resultsController.tableView.reloadData()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.tableView
{
return self.teachers.count
}else
{
return self.filteredTeachers.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = UITableViewCell()
if tableView == self.tableView
{
cell.textLabel?.text = self.teachers[indexPath.row]
}else
{
cell.textLabel?.text = self.filteredTeachers[indexPath.row]
}
return cell
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
DispatchQueue.main.async(){
if tableView == self.tableView
{
name = self.teachers[indexPath.row]
}else
{
name = self.filteredTeachers[indexPath.row]
}
self.performSegue(withIdentifier: "teacherDetail", sender: self)
}
}
}
And here is the swift file for the view controller I'm trying to segue to.
import UIKit
class DirectoryDetailVC: UIViewController{
#IBOutlet weak var test: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
test.text = name
}
}
make sure that your story board View Controller name is teacherDetail - use
DispatchQueue.main.async { self.performSegue(withIdentifier: "teacherDetail", sender: self)}
instead of
self.performSegue(withIdentifier: "teacherDetail", sender: self)
rmaddy caught it. It was a typo. "Typo - change didDeselectRowAtlectRowAt to didSelectRowAt"

Search Bar Glitch

The problem:
Instead of adding a search bar(with a search and results controller) to a table view controller, I have added it to a regular view controller's navigation bar. At first everything seems fine, but when I click on the search bar the screen turns gray.
This is my code:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchResultsUpdating, UISearchBarDelegate{
var schools = ["Saratoga", "Fremont", "Argonaut", "Redwood", "Foothill", "Miller", "Rolling Hills"].sorted()
var filteredSchools = ["Saratoga", "Fremont", "Argonaut", "Redwood", "Foothill", "Miller", "Rolling Hills"].sorted()
var searchController: UISearchController!
var resultsController: UITableViewController!
override func viewDidLoad() {
super.viewDidLoad()
resultsController = UITableViewController()
searchController = UISearchController(searchResultsController: resultsController)
resultsController.tableView.delegate = self
resultsController.tableView.dataSource = self
searchController.searchResultsUpdater = self
self.view.addSubview(searchController.searchBar)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: searchController.searchBar)
}
func updateSearchResults(for searchController: UISearchController) {
let currText = searchController.searchBar.text ?? ""
filteredSchools = schools.filter({ (school) -> Bool in
if school.contains(currText){
return true
}
return false
})
resultsController.tableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredSchools.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = filteredSchools[indexPath.row]
return cell
}
}
Add these lines in viewDidLoad:
resultsController.tableView.backgroundColor = UIColor.clear
searchController.hidesNavigationBarDuringPresentation = false
Your Navigation Bar is hiding that's all.
If you don't want the gray tint:
searchController.dimsBackgroundDuringPresentation = false

Why is my tableView variable returning nil?

I am using a UISearchController in my project. I initiate the search controller by supplying the init(searchResultsController) method with an UIViewController object that manages a tableView. The code for this object is:
import UIKit
class ResultsTableViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var list: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ResultsTableViewController: UITableViewDelegate{}
extension ResultsTableViewController: UITableViewDataSource{
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")
if cell == nil {
cell = UITableViewCell(style: .Default, reuseIdentifier: "Cell")
cell?.textLabel?.text = list[indexPath.row]
} else {
cell?.textLabel!.text = list[indexPath.row]
}
return cell!
}
}
Furthermore, when I try to access the resultsTableViewController.tableView from the updateSearchResultsForSearchController(searchController: UISearchController) method of the UISearchResultsUpdating protocol to populate its "list" array, it gives me an error. The tableView returns nil and the app crashes. I would like to point out that I have connected the data source, delegate, and the IBOutlet variable of the tableView to the appropriate view controller. I was hoping for someone to explain to me why this happens? I think I have a misunderstanding in the life cycle of the ResultsTableViewController. Lastly, when I drag a TableViewController from the storyboard instead of making my own table view controller from scratch everything works smoothly without any errors! Can you please help me understand whats going on here?
Edit: The code for my initial view controller is:
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var resultsTableViewController = ResultsTableViewController()
var searchController: UISearchController!
let list = ["Apple", "Orange", "Bananas","Kiwi", "Cucumbers", "Apricot","Peach", "Cherry", "Mangosteen","Strawberry", "Blueberry", "Raspberry","Watermelon", "Persimmon", "plums","Papaya", "Jackfruit", "Lichi"]
var filteredList: [String]!
override func viewDidLoad() {
super.viewDidLoad()
setUpSearchController()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func setUpSearchController(){
searchController = UISearchController(searchResultsController: resultsTableViewController)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = true
tableView.tableHeaderView = searchController.searchBar
tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
}
}
extension FirstViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell")
if cell == nil {
cell = UITableViewCell(style: .Default, reuseIdentifier: "cell")
cell?.textLabel?.text = list[indexPath.row]
}else {
cell?.textLabel?.text = list[indexPath.row]
}
return cell!
}
}
extension FirstViewController: UISearchResultsUpdating{
func updateSearchResultsForSearchController(searchController: UISearchController) {
filteredList = list.filter({
item in
return item.containsString(searchController.searchBar.text!)
})
resultsTableViewController.list = filteredList
resultsTableViewController.tableView.reloadData()
}
}
var resultsTableViewController = ResultsTableViewController()
creates a new ResultsTableViewController but it isn't linked to your storyboard scene, so none of the #IBOutlets will be set. You need to set an identifier for your scene (say resultsViewController) and then use that to instantiate the view controller from the storyboard:
var resultsTableViewController: ResultsTableViewController!
override func viewDidLoad() {
super.viewDidLoad()
setUpSearchController()
}
func setUpSearchController(){
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
self.resultsTableViewController = storyboard.instantiateViewControllerWithIdentifer("resultsViewController") as! ResultsTableViewController
searchController = UISearchController(searchResultsController: resultsTableViewController)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = true
tableView.tableHeaderView = searchController.searchBar
tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
}

Hide Cancel Button on UISearchController swift 2.0

theres any way to hide the cancel button on UISearchController?
The other behaviour i would like to know if its possible, is when the user press the Cancel Button, to set the text on the UISearchBar.
Thanks
Using seachController.searchBar.setShowsCancelButton(false, animated:false) still seems to not do it for this.
Try adding the UISearchBarDelegate to your controller and assign the searchBar's delegate to self. Then you can use the searchBarShouldBeginEditing to edit its visibility.
import UIKit
class TableViewController: UITableViewController, UISearchBarDelegate {
var searchController: UISearchController!
let menuItems: [String] = ["Row 1", "Row 2", "Row 3"]
override func viewDidLoad() {
super.viewDidLoad()
let searchResultsController = storyboard!.instantiateViewControllerWithIdentifier("SearchResultsViewControllerStoryboardIdentifier") as! SearchResultsTableViewController
searchController = UISearchController(searchResultsController: searchResultsController)
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.delegate = self
}
#IBAction func searchButtonPressed(sender: UIBarButtonItem) {
presentViewController(searchController, animated: true, completion: nil)
}
func searchBarShouldBeginEditing(searchBar: UISearchBar) -> Bool {
searchController.searchBar.setShowsCancelButton(false, animated: true)
return true
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menuItems.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = self.menuItems[indexPath.row]
return cell
}
}

Resources