Swift search bar(controller) memory leaks - ios

I have main screen, with a button on which I segue to searchVC screen.I have a navigation controller between them, in searchVC there are searchController and searchBar.
Problem:I need to activate search when screen appears, but searchBar activation(tap or becomeFirstResponder() ) causes memory leaks(image below)
I tried to remove delegates and the problem disappears, but I need to know when cancel button pressed to segue/dismiss to mainVC
Code:tableView for results, resultView with label for empty results
class SearchViewController: UIViewController,UISearchBarDelegate,UISearchControllerDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var resultView: ResultView!
let searchController = UISearchController(searchResultsController: nil)
var filteredSongs = [SongListModel]()
var songs = SongListModel.fetchSongs()
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search songs"
if #available(iOS 11.0, *) {
navigationItem.titleView = searchController.searchBar
navigationItem.hidesSearchBarWhenScrolling = false
// navigationController?.navigationBar.topItem?.searchController = searchController
// navigationItem.titleView?.isHidden = true
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
} else {
tableView.tableHeaderView = searchController.searchBar
}
searchController.searchBar.showsCancelButton = true
searchController.definesPresentationContext = true
searchController.searchBar.sizeToFit()
searchController.delegate = self
searchController.searchBar.delegate = self
tableView.keyboardDismissMode = .interactive
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
delay(0.1) { [unowned self] in
self.searchController.searchBar.becomeFirstResponder()
}
}
func delay(_ delay: Double, closure: #escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
searchController.searchBar.resignFirstResponder()
}
func searchBarIsEmpty() -> Bool {
// Returns true if the text is empty or nil
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredSongs = songs.filter({( song : SongListModel) -> Bool in
return song.musicFileName.lowercased().contains(searchText.lowercased())
})
tableView.reloadData()
}
func isFiltering() -> Bool {
return searchController.isActive && !searchBarIsEmpty()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchController.searchBar.endEditing(true)
searchController.searchBar.resignFirstResponder()
// searchController.searchBar.delegate = nil
// searchController.searchResultsUpdater = nil
dismiss(animated: true, completion: nil)
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "PlayTilesSegue", let destinationVC = segue.destination as? TilesViewController, let selectedIndex = tableView.indexPathForSelectedRow?.row {
let song: SongListModel
if isFiltering() {
song = filteredSongs[selectedIndex]
} else {
song = songs[selectedIndex]
}
destinationVC.songFileName = song.musicFileName
navigationController?.setNavigationBarHidden(true, animated: false)
}
}
}
extension SearchViewController: UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering() {
resultView.setIsFilteringToShow(filteredItemCount: filteredSongs.count, of: songs.count)
return filteredSongs.count
}
resultView.setNotFiltering()
return songs.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "filteredCell", for: indexPath) as! FilteredSongCell
let song: SongListModel
if isFiltering() {
song = filteredSongs[indexPath.row]
} else {
song = songs[indexPath.row]
}
cell.listenSongButton.setBackgroundImage(UIImage(named: "playback"), for: .normal)
cell.filteredAuthorNameLabel.text = song.authorName
cell.filteredSongNameLabel.text = song.songName
cell.playGameButton.setTitle(song.playButton.rawValue, for: .normal)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "PlayTilesSegue", sender: indexPath)
tableView.deselectRow(at: indexPath, animated: true)
}
}
extension SearchViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
Image memory leaks

Deactivating the search controller on removing VC from parent helps to avoid memory leak:
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
if parent == nil, searchController.isActive {
searchController.isActive = false
}
}

You need to set the UISearchController searchBar's delegate. Once you have done this, the addition of the delegate method searchBarCancelButtonClicked: will properly be called.
Here it is.

Related

UISearchController cancel button dismisses UITabBarController when tapped multiple times in a row

I use a custom search controller that has a tableView for showing the results,
the problem is when tapping the cancel button multiple times it dismisses the tabBarController.
But if i press it normally it acts as it should be.
class UICustomSearchController: UISearchController {
private var suggestedTableView: UITableView!
weak var suggestionDelegate: SearchSuggestionsDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
suggestedTableView = UITableView(frame: CGRect(x: 0, y: searchBar.frame.maxY,
width: view.frame.width,
height: view.frame.height - 70))
suggestedTableView.backgroundColor = UIColor.clear
suggestedTableView.tableFooterView = UIView()
view.subviews.forEach {
if $0.isKind(of: UITableView.self) {
$0.removeFromSuperview()
}
}
view.addSubview(suggestedTableView)
suggestedTableView.dataSource = self
suggestedTableView.delegate = self
suggestedTableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
let tap = UITapGestureRecognizer(target: self, action: #selector(tableTapped))
suggestedTableView.addGestureRecognizer(tap)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
suggestedTableView.removeFromSuperview()
suggestedTableView = nil
dismiss(animated: false, completion: nil)
}
func reloadSuggestions() {
suggestedTableView.reloadData()
}
private func dismissView() {
searchBar.text = ""
suggestedTableView.removeFromSuperview()
dismiss(animated: false, completion: nil)
suggestionDelegate?.didDismissed()
}
// MARK: - Actions
#objc func tableTapped(tap:UITapGestureRecognizer) {
suggestedTableView.removeGestureRecognizer(tap)
let location = tap.location(in: suggestedTableView)
let path = suggestedTableView.indexPathForRow(at: location)
if let indexPathForRow = path {
tableView(suggestedTableView, didSelectRowAt: indexPathForRow)
} else {
dismissView()
}
}
}
extension UICustomSearchController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return suggestionDelegate?.suggestions().count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let list = suggestionDelegate?.suggestions() ?? []
cell.textLabel?.text = list[indexPath.row]
cell.textLabel?.textColor = UIColor.color(from: .blueTabBar)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let list = suggestionDelegate?.suggestions() ?? []
searchBar.text = list[indexPath.row]
searchBar.becomeFirstResponder()
suggestionDelegate?.didSelect(suggestion: list[indexPath.row])
}
}
And in the viewController that has search:
func initSearchViews() {
// Create the search controller and specify that it should present its results in this same view
searchController = UICustomSearchController(searchResultsController: nil)
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.barTintColor = UIColor.white
searchController.searchBar.tintColor = UIColor.color(from: .blueTabBar)
searchController.searchBar.setValue(Strings.cancel.localized, forKey:"_cancelButtonText")
searchController.suggestionDelegate = self
if let searchTextField = searchController!.searchBar.value(forKey: "searchField") as? UITextField {
searchTextField.placeholder = Strings.search.localized
}
// Make this class the delegate and present the search
searchController.searchBar.delegate = self
}
I tried putting this line in viewController but nothing happened:
definesPresentationContext = true

how to auto-paste text in swift?

I have searchbar in my table view I want to auto search or auto-paste text in search bar when we have copied text
can I do it?
it is source code
import UIKit
class TableViewController: UITableViewController, UISearchBarDelegate {
var listEnWord = [EnWordModel]()
var filteredWord = [EnWordModel]()
var inSearchMode = false
var dbHelper = DatabaseHelper()
override func viewDidLoad() {
super.viewDidLoad()
searchaWord()
loadAllWords()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
func loadAllWords(){
listEnWord = dbHelper.getAllEnWord()
tableView.dataSource = self
tableView.delegate = self
}
func searchaWord(){
let searchBar = UISearchBar()
self.view.addSubview (searchBar)
searchBar.delegate = self
searchBar.returnKeyType = UIReturnKeyType.done
navigationItem.titleView = searchBar
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
self.view.endEditing(true)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if inSearchMode {
return filteredWord.count
}
return listEnWord.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! ItemMenuCell
if inSearchMode {
cell.enword = filteredWord[indexPath.row]
} else {
cell.enword = listEnWord[indexPath.row]
}
return cell
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
inSearchMode = false
tableView.reloadData()
self.view.endEditing(true)
} else {
inSearchMode = true
let lower = searchBar.text!.lowercased()
filteredWord = listEnWord.filter({$0.Word?.range(of: lower, options: .anchored ) != nil})
tableView.reloadData()
tableView.setContentOffset(CGPoint.zero, animated: true)
self.view.endEditing(true)
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if inSearchMode {
Singleton.ShareInstance.enwordSelected = filteredWord[indexPath.row]
} else {
Singleton.ShareInstance.enwordSelected = listEnWord[indexPath.row]
}
let des = storyboard?.instantiateViewController(withIdentifier: "DetailController")
navigationController?.pushViewController(des!, animated: true )
}
}
thanks for helping me
Sure, seems like you are looking for a way to auto search text in your clipboard. So first register clipboard text change listener in this way. Note that it only works within your application:
NotificationCenter.default.addObserver(self, selector: #selector(clipboardChanged),
name: NSNotification.Name.UIPasteboardChanged , object: nil)
Then handle the it by this function
func clipboardChanged(){
let pasteboardString: String? = UIPasteboard.general.string
if let theString = pasteboardString {
print("String is \(theString)")
// Put the string into your search bar and do the search
}
}

Screen lags when UISearchController dismissed

Expected:
When a UIButton is tapped, show a viewcontroller modally that has a search controller and tableview with results.
When tapping on an item in the list, change the text of the search bar to what was tapped and dismiss the viewcontroller back to the original with the UIButton now set to that text.
Actual:
UIButton calls a segue to the searchViewController.
searchViewController shows, and configures the searchController and tableView correctly.
Tapping on a cell calls the exit segue that unwinds to the original screen and correctly updates the text in the UIButton and searchbar...
but, a freaking white screen lags on the unwind segue and its driving me crazy.
Mitigation tried:
Resigning the searchController then calling the segue
programmatically
Calling self.dismiss(animated: true completion:nil) in didSelectRowAt
Putting the dismiss on the main thread with: DispatchQueue.main.async { }
calling self.presentingViewController?.dismiss(animated: true)
Video Demo of flashing
Code:
SearchDetailsViewController - the Viewcontroller to unwind to
import UIKit
class SearchDetailsViewController: UIViewController {
#IBOutlet weak var destinationButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if searchDestination != "" {
destinationButton.setTitle(searchDestination, for: UIControlState.normal)
destinationButton.setTitleColor(UIColor.black, for: UIControlState.normal)
} else {
destinationButton.setTitle("Search Nearby", for: UIControlState.normal)
}
}
#IBAction func unwindToSearchDetailsViewController(segue: UIStoryboardSegue){
}
}
SearchViewController - the problem child. I currently have the tableview cell as the exit segue in storyboard.
class SearchViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating, UISearchBarDelegate, UISearchControllerDelegate {
#IBOutlet weak var searchResultsTableView: UITableView!
var destinationsObj:[String:[String]] = [:]
var destinations:[String] = []
var defaultDestinations:[String] = ["Search Nearby"]
var filteredDestinations:[String] = ["Search Nearby"]
var shouldShowSearchResults = false
var searchActive:Bool = false
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
defaultDestinations = recentSearches
configureTableView()
configureSearchController()
}
override func viewDidAppear(_ animated: Bool) {
// Show search bar keyboard
searchController.isActive = true
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: Configure Functions
func configureSearchController() {
searchController = UISearchController(searchResultsController: nil) //nil lets the view controller also be the search results
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Where to?"
searchController.searchBar.delegate = self
searchController.searchBar.sizeToFit()
searchResultsTableView.tableHeaderView = searchController.searchBar
searchController.delegate = self
}
func configureTableView() {
searchResultsTableView.delegate = self
searchResultsTableView.dataSource = self
//searchResultsTableView.isMultipleTouchEnabled = false
}
// MARK: TableView Delegate Functions
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if shouldShowSearchResults {
return filteredDestinations.count
} else {
return defaultDestinations.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "idCell", for: indexPath)
if shouldShowSearchResults {
cell.textLabel?.text = filteredDestinations[indexPath.row]
} else {
cell.textLabel?.text = defaultDestinations[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40.0
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let value = tableView.cellForRow(at: indexPath)?.textLabel?.text {
self.searchController.searchBar.text = value
searchDestination = value
if !recentSearches.contains(value) {
recentSearches.append(value)
}
}
//self.searchController.resignFirstResponder()
// tableView.deselectRow(at: indexPath, animated: false)
// DispatchQueue.main.async {
// self.dismiss(animated: true, completion: nil)
// }
// self.performSegue(withIdentifier: "cancelSearchSegue", sender: self)
}
// MARK: UISearchBar Delegate Functions
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
//self.dismiss(animated: true, completion: nil)
self.performSegue(withIdentifier: "cancelSearchSegue", sender: self)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if let value = searchBar.text {
searchDestination = value
if !recentSearches.contains(value) {
recentSearches.append(value)
}
}
//self.dismiss(animated: true, completion: nil)
self.performSegue(withIdentifier: "cancelSearchSegue", sender: self)
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
shouldShowSearchResults = true
if searchText.characters.count > 1 {
return
} else {
if let firstLetter = searchText.characters.first{
print("Typed \(firstLetter)")
getPredictionData(firstLetter:firstLetter.description)
}
}
}
func dismissCurrentView() {
// self.presentingViewController?.dismiss(animated: true, completion: nil)
self.performSegue(withIdentifier: "cancelSearchSegue", sender: self)
}
Screenshot of my storyboard
Well I thought I post the answer incase this happens to anyone else.
In ViewDidAppear, I was setting the searchController to active
searchController.isActive = true
Well before I dismissed, I needed to set it to inactive!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let value = tableView.cellForRow(at: indexPath)?.textLabel?.text {
self.searchController.searchBar.text = value
searchDestination = value
if !recentSearches.contains(value) {
recentSearches.append(value)
}
}
self.searchController.isActive = false
self.performSegue(withIdentifier: "cancelSearchSegue", sender: self)
}
Don't perform segue, try to dismiss view controller directly and before dismissing set the property of presenting view controller to the search result

SearchController issue, when search the displayController shows a spacing from the Searchbar

The issue is this:
In the storyboard, I must uncheck the Adjust Scroll View Insets, because if not do this, I will get a other issue(https://stackoverflow.com/questions/40974647/uisearchcontroller-issue-nslayoutattribute-do-not-work-in-real-device), and I don't know this if is affect the issue here.(I test in simulator, if check Adjust Scroll View Insets, the issue here will not appear )
My code
import UIKit
import SVProgressHUD
class ChooseStoreViewController: UIViewController,UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {
#IBOutlet weak var tableView: UITableView!
var ori_dataSource: [StoreListModel] = [StoreListModel]()
var dataSource = [String]()
var filterdDataSource = [String]()
var resultSearchController = UISearchController()
var choosedStore:StoreListModel? = nil
var userInfoFromChooseTerant:[String:Any]?
#IBOutlet weak var top_constraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
initData()
initUI()
}
// MARK: - view life
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.isNavigationBarHidden = true
}
func initData() {
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.resultSearchController.searchBar.sizeToFit()
self.resultSearchController.searchBar.placeholder = "search"
self.resultSearchController.searchBar.tintColor = UIColor.black
self.resultSearchController.searchBar.delegate = self
self.tableView.tableHeaderView = self.resultSearchController.searchBar
let nib = UINib(nibName: "TerantListCell", bundle: nil)
// Required if our subclasses are to use: dequeueReusableCellWithIdentifier:forIndexPath:
//tableView.register(nib, forCellReuseIdentifier: "TerantListCell")
self.tableView.register(nib, forCellReuseIdentifier: "TerantListCell")
self.tableView.tableFooterView = UIView.init()
self.tableView.reloadData()
networkForStoreList()
}
func initUI() {
let backNavItem:UIBarButtonItem = UtilSwift.addBackButtonItem(nil, controlelr: self)
backNavItem.action = #selector(navBack)
// print(userInfoFromChooseTerant!)
tableView.separatorStyle = UITableViewCellSeparatorStyle.none
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
let chooseRole: ChooseRoleViewController = segue.destination as! ChooseRoleViewController
chooseRole.userInfoFromChooseStore = self.userInfoFromChooseTerant
}
// MARK: - search delegate
func searchBarCancelButtonClicked() {
for item:NSLayoutConstraint in self.tableView.constraints {
self.view.setNeedsLayout()
if item.firstAttribute == NSLayoutAttribute.top {
item.constant = 0
}
}
}
// MARK: - searchbar delegate
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchBar.setValue("cancel", forKey:"_cancelButtonText")
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
}
// MARK: - private methods
func navBack() {
_ = self.navigationController?.popViewController(animated: true)
}
// MARK: - actions
#IBAction func unwindToChooseStoreVCFromChooseRole(segue: UIStoryboardSegue){
}
#IBAction func nextStepAction(_ sender: UIButton) {
/*if choosedStore == nil {
let lml_alert: LMLDropdownAlertView = LMLDropdownAlertView.init(frame: self.view.bounds)
lml_alert.showAlert(title: Global.hint, detail_Title: "select", cancleButtonTitle: "cacnel", confirmButtonTitle: "confirm", action: { (button) in
})
return
}*/
self.resultSearchController.isActive = false
if self.choosedStore != nil {
_ = self.userInfoFromChooseTerant?.updateValue(self.choosedStore!.userId, forKey: "store_id")
}
self.performSegue(withIdentifier: "ChooseStoreVCToChooseRoleVC", sender: self)
}
// MARK: - network
func networkForStoreList() {
let params:[String:String] = [
"createTime":"-1",
"userId" : self.userInfoFromChooseTerant!["affiliated_id"] as! String
]
// url_terantList
Mysevers.afpost(withHud: true, andAddressname: Global.url_listStore, parmas: params, requestSuccess: { (result) in
let stateCode = UtilSwift.getNetStateCode(result: result as Any, key: Global.net_key_stateCode)
if stateCode == 0 {
let storeArr:[[String : Any]] = UtilSwift.getNetAnyObject(result: result as Any, key: "list") as! [[String : Any]] // Global.net_key_bussines
//self.ori_dataSource = terantArr
for item:[String: Any] in storeArr {
let store_list_model: StoreListModel = StoreListModel.initStoreListModelWithDic(dic: item)
self.ori_dataSource.append(store_list_model)
}
for item:StoreListModel in self.ori_dataSource {
self.dataSource.append(item.name)
}
self.tableView.reloadData()
}else if stateCode == -1 {
SVProgressHUD.showError(withStatus: "err")
}
}, failBlcok: {
SVProgressHUD.showError(withStatus: "err")
})
}
// MARK: - tableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.resultSearchController.isActive {
return filterdDataSource.count
}else {
return dataSource.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: TerantListCell = tableView.dequeueReusableCell(withIdentifier: "TerantListCell", for: indexPath) as! TerantListCell
if self.resultSearchController.isActive {
cell.title_label.text = self.filterdDataSource[indexPath.row]
}else {
cell.title_label?.text = self.dataSource[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.choosedStore = self.ori_dataSource[indexPath.row]
}
// MARK: - regexp
func updateSearchResults(for searchController: UISearchController) {
self.filterdDataSource.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.dataSource as NSArray).filtered(using: searchPredicate)
self.filterdDataSource = array as! [String]
self.tableView.reloadData()
}
}
Go to ".storyboard" file where "ChooseStoreViewController" exist. Then click on UITableView and change tableView constraints as follows:
Check Top Space constraint.

tableview filtered rows get reloaded after selecting row

I have implemented a search bar for my tableview by:
let searchController = UISearchController(searchResultsController: nil)
and in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
tblSearchTable.delegate = self
tblSearchTable.dataSource = self
loadListOfCountries()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tblSearchTable.tableHeaderView = searchController.searchBar
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
}
My problem is that, when I search for items, the table shows the filtered rows but when I tap/click a row, the tableview rows get reloaded (all items). This is my didselectrow function:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("selected row")
self.dismiss(animated: false, completion: nil)
}
The text selected row gets printed but the the viewcontroller isnt getting dismissed but instead, just like I said, reloads all the original items in tableview.
Here are the extensions, might need them when helping me:
#available(iOS 10.0, *)
extension Search: UISearchBarDelegate {
// MARK: - UISearchBar Delegate
func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchText: searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}
#available(iOS 10.0, *)
extension Search: UISearchResultsUpdating {
public func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
}
And here's the search function:
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredArray.removeAll()
var x = 0
for a in dataArray{
if( a.lowercased().contains(searchText.lowercased())){
filteredArray.append(a)
}
x = x + 1
}
self.tblSearchTable.reloadData()
}
Here's the complete code:
import UIKit
import CoreData
#available(iOS 10.0, *)
class Search : UIViewController, UITableViewDelegate, UITableViewDataSource{
var dataArray = [String]()
var dataLine = [String]()
var dataColor = [String]()
var filteredArray = [String]()
var filteredLine = [String]()
var filteredColor = [String]()
let searchController = UISearchController(searchResultsController: nil)
#IBOutlet var tblSearchTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tblSearchTable.delegate = self
tblSearchTable.dataSource = self
loadListOfCountries()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tblSearchTable.tableHeaderView = searchController.searchBar
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: UITableView Delegate and Datasource functions
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("selected row")
self.dismiss(animated: false, completion: nil)
//var ClassViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewController") as! ViewController
//ClassViewController.funcforsearch()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != "" {
return filteredArray.count
}
return dataArray.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "idCell", for: indexPath as IndexPath)
if searchController.isActive && searchController.searchBar.text != "" {
cell.textLabel?.text = filteredArray[indexPath.row]
} else {
cell.textLabel?.text = dataArray[indexPath.row]
}
//cell.textLabel?.text = candy.name
//cell.detailTextLabel?.text = candy.category
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 60.0
}
// MARK: Custom functions
func loadListOfCountries() {
//get some array here
self.tblSearchTable.reloadData()
}
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredArray.removeAll()
var x = 0
for a in dataArray{
if( a.lowercased().contains(searchText.lowercased())){
filteredArray.append(a)
}
x = x + 1
}
self.tblSearchTable.reloadData()
}
}
#available(iOS 10.0, *)
extension Search: UISearchBarDelegate {
// MARK: - UISearchBar Delegate
func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchText: searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}
#available(iOS 10.0, *)
extension Search: UISearchResultsUpdating {
public func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
}
Thanks!

Resources