Screen lags when UISearchController dismissed - ios

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

Related

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.

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"

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

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.

selecting cell in tableview while UISearchController is active, doesnt present next view?

I have made a tableview where you can select a cell, and then the viewcontroller will perform a segue to the next view, which works perfectly fine when you are not using the searchcontroller.
Then when you are using the searchcontroller, it filters the tableview as it should, and the segue is called in didSelectRowAtIndexPath, and the prepareForSegue is called. The problem then is that the view it should segue to is not presented? I can see that the code in the class connected to the view is running, so the segue is performed, it is just the view that does not follow. What am i missing
class CompanyListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {
#IBOutlet weak var tableView: UITableView!
let objectMapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
var activityIndicatorView: SWActivityIndicatorView!
var resultSearchController: UISearchController!
var allCompanies: [Company] = []
var filteredCompanies = [Company]()
override func viewDidLoad() {
super.viewDidLoad()
// set delegates
tableView.delegate = self
tableView.dataSource = self
configureSearchController()
// initialize activity indicator view
self.activityIndicatorView = SWActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
activityIndicatorView.hidesWhenStopped = true
activityIndicatorView.color = UIColor.lightGrayColor()
self.view.addSubview(activityIndicatorView)
self.activityIndicatorView.center = self.view.center
activityIndicatorView.startAnimating()
// fetch all records from backend
fetchAllRecords({(errors: [NSError]?) -> Void in if errors != nil {print(errors)}})
}
func configureSearchController() {
// Initialize and perform a minimum configuration to the search controller.
// Search Bar
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController?.searchBar.autocapitalizationType = .None
self.tableView.tableHeaderView = self.resultSearchController?.searchBar
resultSearchController?.dimsBackgroundDuringPresentation = false
self.resultSearchController?.searchResultsUpdater = self
definesPresentationContext = true
}
// search delegate method
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filterContentForSearchText(searchController.searchBar.text!)
}
// Filter method, which filters by companyName, and reloads tableview
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredCompanies = allCompanies.filter { company in
return company._companyName!.lowercaseString.containsString(searchText.lowercaseString)
}
tableView.reloadData()
}
// fetch all records from backend
func fetchAllRecords(completionHandler: (errors: [NSError]?) -> Void) {
let scanExpression = AWSDynamoDBScanExpression()
objectMapper.scan(Company.self, expression: scanExpression) { (response: AWSDynamoDBPaginatedOutput?, error: NSError?) in
dispatch_async(dispatch_get_main_queue(), {
// if error
if let error = error {
completionHandler(errors: [error]);
}
//if success
else {
self.allCompanies = response!.items as! [Company]
self.tableView.reloadData()
self.activityIndicatorView.stopAnimating()
}
})
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if resultSearchController.active && resultSearchController.searchBar.text != "" {
return filteredCompanies.count
}
return allCompanies.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:CompanyListTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("companyCell") as! CompanyListTableViewCell
// set the text from the data model
let company:Company?
if resultSearchController.active && resultSearchController.searchBar.text != "" {
company = self.filteredCompanies[indexPath.row]
} else {
company = self.allCompanies[indexPath.row]
}
cell.titleLabel.text = company!._companyName
cell.imageview?.image = UIImage(named: "placeholder")
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("segueToProfile", sender: self)
}
// send selected company with segue to profile
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segueToProfile"){
let indexPath = tableView.indexPathForSelectedRow
//tableView.deselectRowAtIndexPath(indexPath!, animated: true)
let selectedRow = indexPath!.row
let profileVC = segue.destinationViewController as! ProfileViewController
if resultSearchController.active{
print(filteredCompanies[selectedRow])
profileVC.company = filteredCompanies[selectedRow]
} else {
profileVC.company = allCompanies[selectedRow]
}
}
}
}
The console is saying this, but i dont know if that has anything to do with this?
2016-11-26 15:54:07.300 Lostandfound[949:2474251] Warning: Attempt to present on which is already presenting
Here is the example of TableView with SearchBar control.You should remove didSelectRowAtIndexPath method and use prepareForSegue method for Determine the selected row in TableView.Like this...
Example:
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate
{
#IBOutlet weak var SerchBar: UISearchBar!
#IBOutlet weak var TableView: UITableView!
var searchActive : Bool = false
var data = ["San Francisco","New York","San Jose","Chicago","Los Angeles","Austin","Seattle"]
var filtered:[String] = []
override func viewDidLoad()
{
super.viewDidLoad()
}
private func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if(searchActive)
{
return filtered.count
}
return data.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = self.TableView.dequeueReusableCell(withIdentifier: "Cell") as! TableViewCell
if(searchActive)
{
cell.Label.text = filtered[indexPath.row]
}
else
{
cell.Label.text = data[indexPath.row]
}
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any!)
{
if let cell = sender as? TableViewCell
{
let i = TableView.indexPath(for: cell)!.row
if segue.identifier == "segue1"
{
if(searchActive)
{
let name1 = segue.destination as! SecondView
name1.str = self.filtered[i]
}
else
{
let name1 = segue.destination as! SecondView
name1.str = self.data[i]
}
}
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
{
filtered = data.filter({ (text) -> Bool in
let tmp: NSString = text as NSString
let range = tmp.range(of: searchText, options: .caseInsensitive)
return range.location != NSNotFound
})
if(filtered.count == 0)
{
searchActive = false;
}
else
{
searchActive = true;
}
self.TableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar)
{
searchActive = true;
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar)
{
searchActive = false;
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar)
{
searchActive = false;
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
{
searchActive = false;
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
}
Your SecondView class is:
import UIKit
class SecondView: UIViewController
{
#IBOutlet weak var label: UILabel!
var str:String!
override func viewDidLoad()
{
super.viewDidLoad()
self.label.text = str
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
}
And your TableViewCell is:
import UIKit
class TableViewCell: UITableViewCell
{
#IBOutlet weak var Label: UILabel!
override func awakeFromNib()
{
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
}

Resources