20

I have two scenes which can be accessed through a tab bar, on scene 1 there is a search bar. the problem that I am facing is that while searching if I switch to the downloads tab -

  1. The navigation bar disappears.
  2. When I come back to the search tab, it gives me a black screen.

This is screen 1 while searching - screen 1 while searching

Now when I click on the downloads tab, the navigation bar disappears.

Here is the view controller for the first screen -

import UIKit
import Alamofire
import SwiftyJSON

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating, UISearchBarDelegate{

    //MARK: Variables
    var papers = [Paper]()
    var filteredPapers = [Paper]()
    let searchController = UISearchController(searchResultsController: nil)

    // MARK: Outlets
    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
    @IBOutlet var table: UITableView!
    @IBOutlet weak var loadingMessageLabel: UILabel!
    @IBOutlet weak var retryButton: UIButton!

    //MARK: Actions
    @IBAction func retryButton(sender: UIButton) {
        self.loadingMessageLabel.hidden = false
        self.loadingMessageLabel.text = "While the satellite moves into position..."
        self.activityIndicator.hidden = false
        self.activityIndicator.startAnimating()
        self.retryButton.hidden = true
        self.getPapersData()

    }

    // MARK: Table View

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // If in searching mode, then return the number of results else return the total number
//        if searchController.active && searchController.searchBar.text != "" {
        if searchController.active {
            return filteredPapers.count
        }
        return papers.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let paper: Paper

//        if searchController.active && searchController.searchBar.text != "" {
        if searchController.active {
            paper = filteredPapers[indexPath.row]
        } else {
            paper = papers[indexPath.row]
        }

        if let cell = self.table.dequeueReusableCellWithIdentifier("Cell") as? PapersTableCell {

            cell.initCell(paper.name, detail: paper.detail)
            print(cell)
            return cell
        }

        return PapersTableCell()

    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    }

    func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {


        let downloadButton = UITableViewRowAction(style: .Normal, title: "Download") { action, index in

            var url = String(self.papers[indexPath.row].url)
            url = url.stringByReplacingOccurrencesOfString(" ", withString: "%20")
            print(url)
            let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)

            // Spinner in cell

            //            var selectCell = self.table.cellForRowAtIndexPath(indexPath) as? PapersTableCell
            //            selectCell!.downloadSpinner.hidden = false

            // Dismiss the download button
            self.table.editing = false

            Alamofire.download(.GET, url, destination: destination).response { _, _, _, error in
                if let error = error {
                    print("Failed with error: \(error)")
                } else {
                    print("Downloaded file successfully")
                }
                //                selectCell?.downloadSpinner.hidden = true
            }

        }

        downloadButton.backgroundColor = UIColor(red:0.30, green:0.85, blue:0.39, alpha:1.0)


        return [downloadButton]

    }

    func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // the cells you would like the actions to appear needs to be editable
        return true
    }

    func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        // you need to implement this method too or you can't swipe to display the actions
    }

    // MARK: Search

    func filterContentForSearchText(searchText: String, scope: String = "All") {
        filteredPapers = papers.filter { paper in
            let categoryMatch = (scope == "All") || (paper.exam == scope)
            return  categoryMatch && paper.name.lowercaseString.containsString(searchText.lowercaseString)
        }

        table.reloadData()
    }

    func updateSearchResultsForSearchController(searchController: UISearchController) {
        let searchBar = searchController.searchBar
        let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
        filterContentForSearchText(searchController.searchBar.text!, scope: scope)

    }

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

    // MARK: Defaults

    override func viewDidLoad() {
        super.viewDidLoad()

        self.getPapersData()

        searchController.searchResultsUpdater = self
        searchController.dimsBackgroundDuringPresentation = false
        definesPresentationContext = true
        table.tableHeaderView = searchController.searchBar
        searchController.searchBar.scopeButtonTitles = ["All", "ST1", "ST2", "PUT", "UT"]
        searchController.searchBar.delegate = self
        activityIndicator.startAnimating()


    }

    override func viewWillDisappear(animated: Bool) {
//        if searchController.active {
            self.searchController.resignFirstResponder()
//        }
    }




    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: API call

    func getPapersData(){
        Alamofire.request(.GET, "http://silive.in/bytepad/rest/api/paper/getallpapers?query=")
            .responseJSON { response in

                self.activityIndicator.stopAnimating()
                self.activityIndicator.hidden = true

                // If the network works fine
                if response.result.isFailure != true {

                    self.loadingMessageLabel.hidden = true
                    self.table.hidden = false
                    //print(response.result)   // result of response serialization

                    let json = JSON(response.result.value!)

                    for item in json {
                        // Split the title on the . to remove the extention
                        let title = item.1["Title"].string!.characters.split(".").map(String.init)[0]
                        let category = item.1["ExamCategory"].string
                        let url = item.1["URL"].string
                        let detail = item.1["PaperCategory"].string

                        let paper = Paper(name: title, exam: category!, url: url!, detail: detail!)
                        self.papers.append(paper)

                    }
                    self.table.reloadData()

                }
                    // If the network fails
                else {
                    self.retryButton.hidden = false
                    self.loadingMessageLabel.text = "Check your internet connectivity"
                }

        }
    }


}
utkbansal
  • 2,787
  • 2
  • 26
  • 47
  • 1
    Put some code for solve this. – Mr. Bond Apr 29 '16 at 12:21
  • 1
    @Mr.Bond added the code. – utkbansal Apr 29 '16 at 12:23
  • 1
    How can you switch to someother tab while searching r u checking in Simulator?Because keyboard will be displayed down you cant switchtabs while searching – Sheereen S Apr 29 '16 at 12:28
  • 1
    @Sherin Yes I am using the simulator, But when i click the search button on the keyboard, it is dismissed. At that point the search controller is still active. Now I can access the tab bar. – utkbansal Apr 29 '16 at 12:28
  • 1
    The simulator has some associated bugs in it. Please check in a real device for actual performance. Even Apple recommends on testing on real device, rather than on a Simulator. Test often and Test early on a real device please. Maybe the problem don't exist in the simulator. – Ariel May 23 '16 at 08:02
  • Can you show the code where you are switching between tabs? It looks like your issue might be related to the tab view controller. – Peter Hornsby May 23 '16 at 10:19

6 Answers6

27

I had this problem in iOS12 and fixed it by setting definesPresentationContext to true for the viewcontroller hosting the search controller.

self.definesPresentationContext = true
rodskagg
  • 3,827
  • 4
  • 27
  • 46
  • Perfectly fixes black screen issue for both cases - when UISearchController is attached to NavigationItem (usually iOS 11+) and when it is attached to table view header (usually iOS 9 and 10). But in case of table view header there might be small layout issues with the table (they can be workarounded or neglected, still much better than having black screen). – Vitalii Nov 30 '18 at 10:08
  • Also fixed the issue that search bar was obscured and dimmed in active state and thus not interactive. – Varrry May 22 '20 at 08:54
9

This is because there is probably no separate navigation controller for the tab that gives you a black screen on back. For maintaining the navigation hierarchy seperately, you should embed a uinavigationcontroller to the uiviewcontroller and from IB check Top Bar as opaque navigation bar instead of Inferred. Hope this helps. Cheerio

Harris
  • 310
  • 3
  • 13
6

Behind the scenes the search controller is being presented. This is what is causing the black screen when returning.

Solution 1

The simplest way is to override UISearchController and set isActive=false in it's viewDidDisappear (https://stackoverflow.com/a/39212080/215748). It works, but I found some usability issues with this solution. I didn't pursue it, so they may be easy enough to overcome.

Solution 2

Call the following before moving away from the view controller:

searchController.dismiss(animated: false, completion: nil)

Add this to viewDidDisappear.

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    dismissSearch()
}

func dismissSearch() {
    searchController.dismiss(animated: false, completion: nil)
}

Unfortunately, viewDidDisappear doesn't get called when you switch tabs, because the search controller is being presented. It received the viewDidDisappear. To get around this, you can subclass UITabBarController and implement UITabBarControllerDelegate.

// don't forget to set the delegate
extension TabBarController: UITabBarControllerDelegate {
    func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
        
        if let navigationController = viewControllers?[selectedIndex] as? UINavigationController {
            for myController in navigationController.viewControllers.flatMap({ $0 as? MyTableViewController }) {
                myController.dismissSearch()
            }
        }
        
        return true
    }
Community
  • 1
  • 1
Onato
  • 9,916
  • 5
  • 46
  • 54
3

Looks like the view that your UISearchController is attached to gets removed from the view hierarchy. You can think of the UISearchController as being presented modally when you start searching, and the definesPresentationContext property indicates which UIViewController would be the one to present it (more on this).

You can get more detail in the answer to a possibly duplicate question: https://stackoverflow.com/a/37357242/300131

Community
  • 1
  • 1
Alex Staravoitau
  • 2,222
  • 21
  • 27
3

Just add UiNavigationcontroller to uiviewcontroller of download tab it will resolve both issue blackout and navigation bar hidden

1

What if you move this bit of code

self.activityIndicator.stopAnimating()
self.activityIndicator.hidden = true

to after your result? Like this:

          // If the network works fine
          if response.result.isFailure != true {

               self.activityIndicator.stopAnimating()
               self.activityIndicator.hidden = true

(if that works, you would also need to include it on the other side of your else too... )

 // If the network fails
                else {
                    self.activityIndicator.stopAnimating()
                    self.activityIndicator.hidden = true

                    self.retryButton.hidden = false
                    self.loadingMessageLabel.text = "Check your internet connectivity"
                }
William GP
  • 1,252
  • 1
  • 14
  • 29