Use of UISearchController causes objc_weak_error - ios

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?

Related

updateSearchResults function is not being called when searchBar is touched

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

iOS UITableView doesn't reload data when searching

I am implementing a search page like Instagram's - such that when you go to the search tab, you see trending searches (with dynamic sections) etc but when you start typing in the search box, the trending searches go away and the search results are shown.
I did just that but it doesn't seem to work.
A) No results are shown when I search for something (I log the response from the api calls - I am getting the data correctly for sure).
B) I don't go back to show trending results even after I hit cancel (Again, I print in the cancelSearch action, the function is being called for sure)
Here's a simplified version of my code:
class SearchVC: UITableViewController {
let searchController = UISearchController(searchResultsController: nil)
var sections = [String]()
var allTrendingOfferings = [[OfferingModel]]()
var allSearchResultOfferings = [OfferingModel]()
override func viewDidLoad() {
super.viewDidLoad()
sections = // data from backend
allTrendingOfferings = // data from backend
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if (searchController.isActive) {
return ""
}
return self.sections[section]
}
override func numberOfSections(in tableView: UITableView) -> Int {
if (searchController.isActive) {
return 1
}
return self.sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (searchController.isActive) {
return allSearchResultOfferings.count
}
return allTrendingOfferings[section].count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "offeringCell", for: indexPath) as! SeachTVCell
if (searchController.isActive) {
let offering = allSearchResultOfferings[indexPath.row]
} else {
let offering = allTrendingOfferings[indexPath.section][indexPath.row]
}
// configure cell and
return cell
}
func filterContentForSearchText(searchText: String, scope: String = "All") {
allSearchResultOfferings = // data fetched from backend
self.tableView.reloadData() // done asynchronously (after receiving data)
}
func searchCancel() {
self.tableView.reloadData()
}
}
extension SearchVC: UISearchResultsUpdating {
#available(iOS 8.0, *)
func updateSearchResults(for searchController: UISearchController){
if !searchController.isActive {
searchCancel()
}
filterContentForSearchText(searchText:searchController.searchBar.text!)
}
}
False alarm - Had to quit Xcode, clean and rebuild the project. It works like a charm now.

Using searchController to filter tableView, but the tableView isn't updating

I have a UITableViewController that is displaying the titles of Tags I created. When I first navigate to the UITableViewController, it displays the Array of Tags just fine, but when I use the UISearchController to filter through Tags, the Array I created to store the filtered results updates and holds the correct data, but the TableView doesn't change. here are the two functions that are most likely causing the problem, but just in case, I will have the entire class (not long) down below.
numberOfRowsInSection:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchController.searchBar.text != "") {
return filteredTags.count
}
return Tags.count
}
cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tagcell", for: indexPath) as! TagCell
var text = ""
if (searchController.searchBar.text != ""){
text = filteredTags[indexPath.row].title
} else {
text = Tags[indexPath.row].title
}
cell.cellLabel.text = text
return cell
}
Whole Class:
class TagCell: UITableViewCell{
#IBOutlet weak var cellLabel: UILabel!
}
class TagTableVC: UITableViewController{
//Table Content
var Tags: [Tag] = [globTS.animals, globTS.civilrights, globTS.guncontrol, globTS.gunrights, globTS.LGBTQ, globTS.prochoice, globTS.prolife]
var filteredTags = [Tag]()
//Searchbar Initialization
let searchController = UISearchController(searchResultsController: nil)
//Required Functions
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchController.searchBar.text != "") {
return filteredTags.count
}
return Tags.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tagcell", for: indexPath) as! TagCell
var text = ""
if (searchController.searchBar.text != ""){
text = filteredTags[indexPath.row].title
} else {
text = Tags[indexPath.row].title
}
cell.cellLabel.text = text
return cell
}
//Filters Tags array into Filtered array based on search query
func filterContentForSearchText(searchText: String, scope: String = "All"){
filteredTags = Tags.filter{ $0.title.lowercased().contains(searchText.lowercased())}
}
}
extension TagTableVC: UISearchResultsUpdating {
//calls the filter function everytime the searchbar is activated
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
}
After reevaluating the filteredTags, you should call reloadData on your tableview
func filterContentForSearchText(searchText: String, scope: String = "All"){
filteredTags = Tags.filter{ $0.title.lowercased().contains(searchText.lowercased())}
self.tableView.reloadData()
}

Searching TableView can't select row

While searching a tableView, every time I try to select a row it just takes me back to the unsearched tableView. What am I missing? the segue works perfectly when not filtering through the table. The ability to select a row just disapears while the searchBar is activated.
import UIKit
import Foundation
class BenchmarkWODViewController: UITableViewController, UISearchResultsUpdating {
var WodList = [WOD]()
var FilteredWodList = [WOD]()
var Keyword = ""
var searchController : UISearchController?
var index = Int()
#IBAction func backButton(sender: AnyObject) {
self.navigationController?.popViewControllerAnimated(true)
}
override func viewDidLoad() {
super.viewDidLoad()
for wodData in BenchmarkWODs.library {
let wod = WOD(dictionary: wodData)
WodList.append(wod)
}
// Search Bar
self.searchController = UISearchController(searchResultsController: nil)
self.searchController?.searchBar.autocapitalizationType = .None
self.tableView.tableHeaderView = self.searchController?.searchBar
self.searchController?.searchResultsUpdater = self
self.Keyword = ""
definesPresentationContext = true
self.filterByName()
}
func filterByName(){
self.FilteredWodList = self.WodList.filter({ (wod: WOD) -> Bool in
if self.Keyword.characters.count == 0 {
return true
}
if (wod.name?.lowercaseString.rangeOfString(self.Keyword.lowercaseString)) != nil {
return true
}
return false
})
self.tableView.reloadData()
}
// Search Bar Function
func updateSearchResultsForSearchController(searchController: UISearchController) {
Keyword = searchController.searchBar.text!
self.filterByName()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.FilteredWodList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("BenchmarkCell", forIndexPath: indexPath) as UITableViewCell
let wod = self.FilteredWodList[indexPath.row]
if let wodName = wod.name {
cell.textLabel?.text = wodName
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.filterByName()
self.performSegueWithIdentifier("showBenchmarkDetail", sender: nil)
}
}
Figured it out after playing around. Apparently adding the below code corrects the problem.
searchController?.dimsBackgroundDuringPresentation = false
swift 'dimsBackgroundDuringPresentation' was deprecated in iOS 12.0 Use the obscuresBackgroundDuringPresentation property instead.
searchController?.obscuresBackgroundDuringPresentation = false
searchController.obscureBAckgroundDuringPresentation = false is deprecated in IOS 12.0, so for me it was issue with other gesture detector added to the tableview , so make sure you dont have any other gesture detector and touchesview method that distort the normal working flow of tablview's delegate method( didSelectAtRow ), hope it will work,

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