Segue from search results view controller to another view controller

There is a search controller with two scopes of decks and cards. I set up a search for these two models, but now the task is that when I tap on these results, for decks should be one segue for cards another. In the pictures, you can see, after I typed search text, it shows results correctly, but when you tap on the result row, nothing happens. Can't find any solution for almost a week...

class DecksTableViewController: UITableViewController {
	private var searchController: UISearchController!
	private var decks: Results<Deck>!

  override func viewDidLoad() {
    super.viewDidLoad()
		tableView.tableFooterView = UIView()
		let resultsTableController = self.storyboard?.instantiateViewController(withIdentifier: "ResultsTableViewController") as! ResultsTableViewController
		configureSearchController(resultsTableController)
		definesPresentationContext = true
		navigationItem.hidesSearchBarWhenScrolling = true
		navigationController?.navigationBar.prefersLargeTitles = true
  }
	
	override func viewWillAppear(_ animated: Bool) {
		super.viewWillAppear(animated)
		loadDecks()
	}
	
	private func loadDecks() {
		decks = StorageManager.realm.objects(Deck.self).sorted(byKeyPath: "dateCreated", ascending: true)
		tableView.reloadData()
	}
	
	private func configureSearchController(_ searchResultsController: ResultsTableViewController) {
		searchController = UISearchController(searchResultsController: searchResultsController)
		searchController.searchResultsUpdater = searchResultsController
		searchController.obscuresBackgroundDuringPresentation = false
		searchController.searchBar.placeholder = "Search"
		searchController.searchBar.scopeButtonTitles = ["Decks", "Cards"]
		searchController.searchBar.delegate = searchResultsController
		navigationItem.searchController = searchController
	}
}

Here the search results controller:

import UIKit
import RealmSwift


class ResultsTableViewController: UITableViewController {
	
	var filteredDecks: Results<Deck>?
	var filteredCards: Results<Card>?
	var scope: String?
	
  override func viewDidLoad() {
    super.viewDidLoad()
		tableView.delegate = self
		tableView.tableFooterView = UIView()
  }

  // MARK: - Table view data source

  override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
		if scope == "Decks" {
			return filteredDecks?.count ?? 0
		} else {
			return filteredCards?.count ?? 0
		}
  }
	

   
  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "resultsCell", for: indexPath)
		var content = cell.defaultContentConfiguration()
		if scope == "Decks" {
			let deck = filteredDecks?[indexPath.row]
			content.text = deck?.name
		} else {
			let card = filteredCards?[indexPath.row]
			content.text = card?.front
			content.secondaryText = card?.back
		}
		
		cell.contentConfiguration = content
    return cell
  }
	
	override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
		if scope == "Decks" {
			guard let cardsVC = self.storyboard?.instantiateViewController(identifier: "cardsVC")
					as? CardsTableViewController
			else {
				return
			}
			cardsVC.selectedDeck = filteredDecks?[indexPath.row]
			self.parent?.navigationController?.pushViewController(cardsVC, animated: true)

		} else {
			guard let cardDetailVC = self.storyboard?.instantiateViewController(identifier: "cardDetailVC")
					as? NewCardViewController
			else {
				return
			}
			cardDetailVC.selectedCard = filteredCards?[indexPath.row]
			self.navigationController?.pushViewController(cardDetailVC, animated: true)
		}
		tableView.deselectRow(at: indexPath, animated: true)
	}
}


extension ResultsTableViewController: UISearchResultsUpdating {
	
	func updateSearchResults(for searchController: UISearchController) {
		let searchBar = searchController.searchBar
		let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
		filterContentForSearchText(searchController.searchBar.text!, scope: scope)
	}
	
	private func filterContentForSearchText(_ searchText: String, scope: String) {
		
		var decks: Results<Deck>?
		var cards: Results<Card>?
		if scope == "Cards" {
			cards = StorageManager.realm.objects(Card.self).sorted(byKeyPath: "front")
			filteredCards = cards?.filter("front CONTAINS[c] '\(searchText)'")
		} else {
			decks = StorageManager.realm.objects(Deck.self).sorted(byKeyPath: "name")
			filteredDecks = decks?.filter("name CONTAINS[c] '\(searchText)'")
		}
		self.scope = scope
		tableView.reloadData()
	}
}

extension ResultsTableViewController: UISearchBarDelegate {
	
	func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
		filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
	}
}

Answered by Claude31 in 681494022

Thanks for additional information.

You notice that text is not formatted in comments, so I reformat here:

Where is result row (a cell in which table) ? I suppose it is in the table of ResultsTableViewController ? Do I understand correctly that this table can contain either Decks or cards ? 

  • Yep, right, results are cells in ResultsTableViewController, there might be decks or cards, depending on scope. 

Is behaviour different for decks or cards, or is it the same "nothing happens" ? 

  • Yes, for both nothing happens. 

Did you check identifiers are effectively "cardsVC" and "cardDetailVC" (with same upper/lowercase) -

Yes, I checked.  This is the output:

navigation controller is nil, I also tried to embed ResultsTableViewController with navigation controller, but it didn't help. 

        print("didSelect scope", scope, "indexPath", indexPath)
  • didSelect scope Optional("Decks") indexPath [0, 0]
            print("cardsVC instantiated, with selectedDeck", cardsVC.selectedDeck)
            print("self.parent?.navigationController", self.parent?.navigationController)
  • Decks cardsVC instantiated, with selectedDeck Optional(Deck { _id = 60e98c5da7d8064df3298f68; name = Greetings; dateCreated = 2021-07-10 12:02:37 +0000; layout = frontToBack; autoplay = 0; cards = List <0x283afcfc0> ( [0] Card { _id = 60e98c63a7d8064df3298f6c; front = hello; back = ; dateCreated = 2021-07-10 12:02:43 +0000; audioName = (null); }, [1] Card { _id = 60e98c6aa7d8064df3298f6d; front = good evening; back = ; dateCreated = 2021-07-10 12:02:50 +0000; audioName = (null); } ); })
  • self.parent?.navigationController nil
  • didSelect scope Optional("Cards") indexPath [0, 0] Cards cardDetailVC instantiated, with selectedCard Optional(Card { _id = 60e98c6aa7d8064df3298f6d; front = good evening; back = ; dateCreated = 2021-07-10 12:02:50 +0000; audioName = (null); })
  • self.navigationController nil

Now the cause: ResultsTableViewController is not part of the navigation stack, hence its navigationController is nil. In fact, you instantiate directly:

let resultsTableController = self.storyboard?.instantiateViewController(withIdentifier: "ResultsTableViewController") as! ResultsTableViewController

You could try to add a property in

class ResultsTableViewController: UITableViewController {
	var originatingController: DecksTableViewController?
	var filteredDecks: Results<Deck>?

and set it when you create resultsTableController

class DecksTableViewController: UITableViewController {
	private var searchController: UISearchController!
	private var decks: Results<Deck>!

  override func viewDidLoad() {
    super.viewDidLoad()
		tableView.tableFooterView = UIView()
		let resultsTableController = self.storyboard?.instantiateViewController(withIdentifier: "ResultsTableViewController") as! ResultsTableViewController
        resultsTableController?.originatingController = self
		configureSearchController(resultsTableController)

Then call it as:

			self.originatingController?.navigationController?.pushViewController(cardsVC, animated: true)

I could not test, but that should be the logic (may need some tuning though).

when you tap on the result row, nothing happens

Where is result row (a cell in which table) ? I suppose it is in the table of ResultsTableViewController ? Do I understand correctly that this table can contain either Decks or cards ?

Is behaviour different for decks or cards, or is it the same "nothing happens" ?

Did you check identifiers are effectively "cardsVC" and "cardDetailVC" (with same upper/lowercase)

Could you instrument the code and tell what you get:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        print("didSelect scope", scope, "indexPath", indexPath)
		if scope == "Decks" {
            print("Decks")
			guard let cardsVC = self.storyboard?.instantiateViewController(identifier: "cardsVC")
					as? CardsTableViewController
			else {
                print("cardsVC was not instantiated")
				return
			}
			cardsVC.selectedDeck = filteredDecks?[indexPath.row]
            print("cardsVC instantiated, with selectedDeck", cardsVC.selectedDeck)
            print("self.parent?.navigationController", self.parent?.navigationController)
			self.parent?.navigationController?.pushViewController(cardsVC, animated: true)

		} else {
            print("Cards")
			guard let cardDetailVC = self.storyboard?.instantiateViewController(identifier: "cardDetailVC")
					as? NewCardViewController
			else {
                print("cardDetailVC was not instantiated")
				return
			}

			cardDetailVC.selectedCard = filteredCards?[indexPath.row]
            print("cardDetailVC instantiated, with selectedCard", cardDetailVC.selectedCard)
            print("self.navigationController", self.navigationController)
			self.navigationController?.pushViewController(cardDetailVC, animated: true)
		}
		tableView.deselectRow(at: indexPath, animated: true)
	}
}
Accepted Answer

Thanks for additional information.

You notice that text is not formatted in comments, so I reformat here:

Where is result row (a cell in which table) ? I suppose it is in the table of ResultsTableViewController ? Do I understand correctly that this table can contain either Decks or cards ? 

  • Yep, right, results are cells in ResultsTableViewController, there might be decks or cards, depending on scope. 

Is behaviour different for decks or cards, or is it the same "nothing happens" ? 

  • Yes, for both nothing happens. 

Did you check identifiers are effectively "cardsVC" and "cardDetailVC" (with same upper/lowercase) -

Yes, I checked.  This is the output:

navigation controller is nil, I also tried to embed ResultsTableViewController with navigation controller, but it didn't help. 

        print("didSelect scope", scope, "indexPath", indexPath)
  • didSelect scope Optional("Decks") indexPath [0, 0]
            print("cardsVC instantiated, with selectedDeck", cardsVC.selectedDeck)
            print("self.parent?.navigationController", self.parent?.navigationController)
  • Decks cardsVC instantiated, with selectedDeck Optional(Deck { _id = 60e98c5da7d8064df3298f68; name = Greetings; dateCreated = 2021-07-10 12:02:37 +0000; layout = frontToBack; autoplay = 0; cards = List <0x283afcfc0> ( [0] Card { _id = 60e98c63a7d8064df3298f6c; front = hello; back = ; dateCreated = 2021-07-10 12:02:43 +0000; audioName = (null); }, [1] Card { _id = 60e98c6aa7d8064df3298f6d; front = good evening; back = ; dateCreated = 2021-07-10 12:02:50 +0000; audioName = (null); } ); })
  • self.parent?.navigationController nil
  • didSelect scope Optional("Cards") indexPath [0, 0] Cards cardDetailVC instantiated, with selectedCard Optional(Card { _id = 60e98c6aa7d8064df3298f6d; front = good evening; back = ; dateCreated = 2021-07-10 12:02:50 +0000; audioName = (null); })
  • self.navigationController nil

Now the cause: ResultsTableViewController is not part of the navigation stack, hence its navigationController is nil. In fact, you instantiate directly:

let resultsTableController = self.storyboard?.instantiateViewController(withIdentifier: "ResultsTableViewController") as! ResultsTableViewController

You could try to add a property in

class ResultsTableViewController: UITableViewController {
	var originatingController: DecksTableViewController?
	var filteredDecks: Results<Deck>?

and set it when you create resultsTableController

class DecksTableViewController: UITableViewController {
	private var searchController: UISearchController!
	private var decks: Results<Deck>!

  override func viewDidLoad() {
    super.viewDidLoad()
		tableView.tableFooterView = UIView()
		let resultsTableController = self.storyboard?.instantiateViewController(withIdentifier: "ResultsTableViewController") as! ResultsTableViewController
        resultsTableController?.originatingController = self
		configureSearchController(resultsTableController)

Then call it as:

			self.originatingController?.navigationController?.pushViewController(cardsVC, animated: true)

I could not test, but that should be the logic (may need some tuning though).

Segue from search results view controller to another view controller
 
 
Q