Unable to segue to VC without dismissing SearchController - ios

I'm unable to transition to my View Controller without having dismissed my Search Controller. My search controller is embedded in my TableView Header.
Is there a way of transitioning without having dismissed my Search Controller first?
Here is some code:
class ViewProfileViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource, UICollectionViewDelegateFlowLayout,
UICollectionViewDataSource, UISearchBarDelegate, UISearchResultsUpdating {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
searchController.searchBar.searchBarStyle = .minimal
searchController.searchBar.placeholder = "Search City"
searchController.searchBar.showsCancelButton = true
searchController.searchBar.delegate = self
searchController.searchBar.backgroundColor = UIColor.white
self.myTable.tableHeaderView = searchController.searchBar
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
// Get rid of searchController
searchController.searchBar.endEditing(true)
searchController.isActive = false
searchController.dismiss(animated: true) { /* */ }
// Deselect row
tableView.deselectRow(at: indexPath, animated: true)
// Present your VC here
let mViewController = MController()
let navController = UINavigationController(rootViewController:
mViewController)
present(navController,animated: true, completion: nil)
}
}

Related

swift table view is locked when searchbar active

i'm using a large navigation bar which includes a searchcontroller. If i dont search i can scroll through my tableview without problems, but if i'm searching it seams like its locked. Here is my code:
func updateSearchResults(for searchController: UISearchController) {
// First we will check if input is only containing numbers => search for PLZ otherwise we will check if a restaurant is called like this otherwise search if there is a suitable city
self.navigationItem.title = searchController.searchBar.text!
if !searchController.isActive{
//TODO get all restaurants for default city
self.navigationItem.title = "München"
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "partnerscell", for: indexPath) as! PartnersCellTableViewCell
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var bounds = UIScreen.main.bounds
var width = bounds.size.width
var height = bounds.size.height
return height/2.2
}
#IBOutlet weak var tv: UITableView!
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.prefersLargeTitles = true
}
self.navigationController?.navigationBar.isTranslucent = true
self.navigationItem.searchController = searchController
self.navigationController?.navigationBar.shadowImage = UIImage()
}
Also if i'm searching my toolbar looks darker. Please find attached two screenshots:
You can't access the table view, because there is an invisible layer over it caused by wrong configuration of UISearchController.
Modify the searchController this way:
let searchController = UISearchController(searchResultsController: nil)
searchController.obscuresBackgroundDuringPresentation = false
searchController.definesPresentationContext = true
You can not access tableview when your search is active because attribute of UISearchController obscuresBackgroundDuringPresentation is default true which indicating underlying content of controller is obscured when search is active. So you can set as follow:
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.prefersLargeTitles = true
}
self.navigationController?.navigationBar.isTranslucent = true
self.navigationItem.searchController = searchController
self.navigationController?.navigationBar.shadowImage = UIImage()
searchController.obscuresBackgroundDuringPresentation = false
searchController.definesPresentationContext = true
}

Swift search bar(controller) memory leaks

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.

iOS 11.0 push view controller problem with navigationItem.searchController

I tried to push a child view controller with the searchBar display, there is a flashing gray bar appeared during the animation (see Figure 2), how can I fix that animation? Thank you.
Code:
override func viewDidLoad() {
super.viewDidLoad()
if title == nil {
title = "Title"
}
if #available(iOS 11.0, *) {
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.searchController = searchController
}
definesPresentationContext = true
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let vc = ViewController()
vc.hidesBottomBarWhenPushed = true
navigationController?.pushViewController(vc, animated: true)
}
If you're trying to push the same ViewController() which is embedded with NavigationController, that bar you saw might be the navigationBar of your ViewController().
Try to create a new controller, then push to see whether the bar still exists, like so:
class NewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
}
}
Then in your ViewController's didSelectRowAt, push the NewController, like so:
let newVC = NewController()
navigationController?.pushViewController(newVC, animated: true)

How to display search results like the iOS "Contacts" app

I am new iOS programming. I have tried to implement my app to look like the iOS Contacts app. But I have no idea how to implement what I desire. I want to get search results to look like the default iOS app.
Look what I tried so far:
When I type something the dimsBackgroundDuringPresentation is still true.
Here is my expectation:
I am wondering how this app shows results like this.
Here is how i declared UISearchController
lazy var searchController: UISearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.hidesNavigationBarDuringPresentation = false
controller.searchBar.sizeToFit()
controller.searchBar.backgroundColor = UIColor.clear
controller.searchBar.placeholder = "Search"
controller.dimsBackgroundDuringPresentation = true
return controller
})()
Here is how i initialized searchBar to header section of tableView
let searchBar = searchController.searchBar
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
searchBar.delegate = self
tableView.tableHeaderView = searchController.searchBar
Here is function for delegate UISearchResultsUpdating
func updateSearchResults(for searchController: UISearchController) {
if let count = searchController.searchBar.text?.count {
if count > 0 {
filterArray.removeAll(keepingCapacity: false)
var a = [String]()
a = [searchController.searchBar.text!]
filterArray = a
searchController.dimsBackgroundDuringPresentation = false
tableView.reloadData()
}else {
searchController.dimsBackgroundDuringPresentation = true
filterArray.removeAll(keepingCapacity: false)
filterArray = array
tableView.reloadData()
}
}
}
Here is my tableView cell looks like
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive {
return filterArray.count
}else {
return array.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
if searchController.isActive {
cell.textLabel?.text = filterArray[indexPath.row]
}else {
cell.textLabel?.text = array[indexPath.row]
}
return cell
}
Try with these options:
let search = UISearchController(searchResultsController: nil)
self.definesPresentationContext = true
search.dimsBackgroundDuringPresentation = false
navigationItem.searchController = search
navigationItem.hidesSearchBarWhenScrolling = false
Try the code in my repo to see it working just tap a tableview item and it will segue to the second controller where you can see the searchbar and tap it to see if this is what you wanted
github link
if navigationItem.searchController == nil {
if searchController == nil {
searchController = UISearchController(searchResultsController: nil)
searchController?.searchBar.placeholder = "Search"
searchController?.delegate = self
searchController?.searchResultsUpdater = self
searchController?.dimsBackgroundDuringPresentation = false
searchController?.searchBar.searchBarStyle = .minimal
}
self.navigationItem.searchController = searchController
self.navigationItem.hidesSearchBarWhenScrolling = false
}
Use property hidesSearchBarWhenScrolling to make the search bar not scroll up, like the contacts app.

Cannot Select TableViewCell when Search Bar is active - iOS Swift

I have a search bar that is connected to a table view. It works exactly as I want when the search bar is not active but cell selection is disabled when the search bar is active. I've debugged it and didSelectRowAtIndexPath is not even being called when I select a row when search is active. What could be causing this?
Here's the relevant code:
class FabricsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UISearchControllerDelegate, UISearchDisplayDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.definesPresentationContext = false
searchController.hidesNavigationBarDuringPresentation = false
myTableView.tableHeaderView = searchController.searchBar
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if searching {
searching = false
searchBar?.resignFirstResponder()
FirebaseClient.sharedInstance.joinFabric(uid: self.appDelegate.uid!, fabricKey: allFabrics[indexPath.row].key)
updateFabricList()
} else {
appDelegate.selectedFabricKey = joinedFabrics[indexPath.row].key
performSegue(withIdentifier: "fabricSelected", sender: self)
}
myTableView.deselectRow(at: indexPath, animated: false)
}
func filterContentForSearchText(searchText: String, scope: String = "All") {
if let allFabrics = allFabrics {
filteredFabrics = allFabrics.filter { fabric in
return (fabric.name.lowercased().contains(searchText.lowercased()))
}
myTableView.reloadData()
myTableView.setContentOffset(CGPoint.zero, animated: false)
}
}
}
extension FabricsViewController: UISearchResultsUpdating {
func updateSearchResults(for: UISearchController) {
filterContentForSearchText(searchText: searchController.searchBar.text!)
}
}
Please check the text for your method name.
Make sure that you are using method name as below:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
You have set the delegate of searchBar to self. Are you activating any tap gestures when search becomes active? This tap gesture can interrupt your didSelectRowAtIndexPath method.
For those who are facing same issue, according Apple's documentation you should have following code for add searchController:
if #available(iOS 11.0, *) {
navigationItem.searchController = searchController
} else {
self.yourTableView.tableHeaderView = searchController.searchBar
}

Resources