2

I'm working on search bar with autofill feature. Sometimes I get this errors when tapping cell in autofill TableView:

*** Assertion failure in -[UITableViewRowData rectForRow:inSection:heightCanBeGuessed:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3505.16/UITableViewRowData.m:1849

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for rect at invalid index path ( {length = 2, path = 0 - 3})'

If I comment this line searchBar.text = cell.textLabel!.text app doesn't crash.

Here is full function code:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        let cell: UITableViewCell = tableView.cellForRow(at: indexPath)!
        address = cell.textLabel!.text!
        searchBar.text = cell.textLabel!.text
    }

How can I fix it?

UPD: Looks like it crashes after searchBar.text = cell.textLabel!.text
I've added print(searchBar.text!) and it prints correct value to console

UPD2: App crashes only if I type something in search bar, then tap somewhere on the screen to dismiss keyboard, and then tap on one of autofill cells.

Class code:

import UIKit
import Alamofire
import SwiftyJSON

class StreetSelectViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate, UINavigationControllerDelegate
{
    var data: [String] = ["Нет данных"]
    var filtered: [String] = []
    var searchActive : Bool = false

    @IBOutlet weak var cityNameLabel: UILabel!
    @IBOutlet weak var searchBar: UISearchBar!
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var topSpaceConstraint: NSLayoutConstraint!
    @IBOutlet var gradientBackground: GradientView!

    let segueIdentifier = "ShowStreetSelectSegue"

    //background gradient
    override func viewDidLayoutSubviews()
    {
        self.gradientBackground.create()
    }

    override func viewDidLoad()
    {
        super.viewDidLoad()

        //side menu panGestureRecognizer
        if self.revealViewController() != nil
        {
            self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
        }

        tableView.delegate = self
        tableView.dataSource = self
        searchBar.delegate = self
        tableView.tableFooterView = UIView()
        navigationController?.delegate = self

        //search bar settings
        let barImage = UIImage()
        searchBar.setBackgroundImage(barImage, for: .any, barMetrics: .default)
        searchBar.scopeBarBackgroundImage = barImage
        searchBar.tintColor = UIColor.lightGray
        searchBar.setValue("Отмена", forKey:"_cancelButtonText")
        searchBar.showsCancelButton = false

        topSpaceConstraint.constant = self.navigationController!.navigationBar.frame.height + 8

        //network
        let queue = DispatchQueue(label: "com.admin.response-queue", qos: .utility, attributes: [.concurrent])
        URLCache.shared.removeAllCachedResponses()
        Alamofire.request("").validate()
            .responseJSON(
                queue: queue,
                completionHandler: { response in
                    if let JSON = response.result.value
                    {
                        self.data.removeAll()
                        let json = SwiftyJSON.JSON(JSON)
                        print("JSON: \(json)")
                        for (_, object) in json
                        {
                            self.data.append(object.stringValue)
                            print(self.data)
                        }
                    }
                    DispatchQueue.main.async {
                        self.tableView.reloadData()
                    }
            }
        )
    }

    override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() }

    override func viewWillAppear(_ animated: Bool)
    {
        cityNameLabel.text = cityName
    }

    //MARK: - search bar settings
    func searchBarTextDidBeginEditing(_ searchBar: UISearchBar)
    {
        searchActive = true;
        self.navigationController?.isNavigationBarHidden = true
        topSpaceConstraint.constant = 8
    }

    func searchBarTextDidEndEditing(_ searchBar: UISearchBar)
    {
        searchActive = false;
        self.navigationController?.isNavigationBarHidden = false
    }

    func searchBarCancelButtonClicked(_ searchBar: UISearchBar)
    {
        searchActive = false;
        filtered = data
        tableView.isHidden = false
        tableView.reloadData()
        topSpaceConstraint.constant = 8
    }

    func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
    {    
        if !searchActive
        {
            searchActive = true
            tableView.reloadData()
        }

        self.searchBar.resignFirstResponder()
        address = searchBar.text!
        performSegue(withIdentifier: "ShowSearchResultsSeque", sender: Any?.self)
    }

    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: NSString.CompareOptions.caseInsensitive)
            return range.location != NSNotFound
        })
        if(filtered.count == 0)
        {
            searchActive = false;
        }
        else
        {
            searchActive = true;
        }

        if searchText.characters.count != 0
        {
            tableView.isHidden = true
        }
        else
        {
            tableView.isHidden = false
        }

        tableView.reloadData()
        topSpaceConstraint.constant = 8
    }

    //MARK: - tableview settings

    func numberOfSections(in tableView: UITableView) -> Int
    {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        if searchActive
        {
            if filtered.count == 0
            {
                return data.count
            }
            else
            {
                return filtered.count
            }
        }
        else
        {
            return data.count
        }
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = self.tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        if searchActive
        {
            if filtered.count == 0
            {
                cell.textLabel?.text = data.sorted()[indexPath.row]
            }
            else
            {
                cell.textLabel?.text = filtered.sorted()[indexPath.row]
            }
        }
        else
        {
            cell.textLabel?.text = data.sorted()[indexPath.row]
        }
        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        let cell: UITableViewCell = self.tableView.cellForRow(at: indexPath)!
        address = cell.textLabel!.text!
        self.searchBar.text = cell.textLabel!.text
        print(searchBar.text!)          
    }

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
    {
        cell.backgroundColor = UIColor.clear
        //text color
        cell.textLabel!.textColor = UIColor.white;
        //cell selection color
        let bgColorView = UIView()
        bgColorView.backgroundColor = UIColor.black.withAlphaComponent(0.3)
        cell.selectedBackgroundView = bgColorView
        tableView.backgroundColor = UIColor.clear
    }                 
}
BadCodeDeveloper
  • 455
  • 5
  • 24

2 Answers2

1

Why you are using self.tableView.cellForRow in didSelectRowAt method, you can use your filtered datasource in didSelectRowAt method, which you have already used in cellForRowAt method. you can do following to achieve your functionality.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        if searchActive
        {
            if filtered.count == 0
            {
                address = data.sorted()[indexPath.row]
            }
            else
            {
                address = filtered.sorted()[indexPath.row]
            }
            self.searchBar.text = address
        }         
    }
Dhaval Dobariya
  • 171
  • 1
  • 12
  • App doesn't crash any more, but autofill works only when keyboard is shown. When keyboard hides, app doesn't react on cell taps. What can be the problem? – BadCodeDeveloper Jan 12 '17 at 07:16
  • Try one more thing, remove `searchActive` boolean flag, and check if your `filtered.count > 0` then only assign value to `address ` and `self.searchBar ` – Dhaval Dobariya Jan 12 '17 at 07:23
  • Autofill stop works because of your `searchActive` is `false` when your search bar/field don't have focus. So you have to remove that unnecessary flag. And maintain single data source in `NSMutableArray` which need to use at all places like `cellForRowAt `, `didSelectRowAt `. and fill it on `viewDidLoad` when you receive data and change it on `textDidChange `. This is more simple way rather than maintaining flag. – Dhaval Dobariya Jan 12 '17 at 12:19
  • Thanks for your help. I'm not sure why but the problem was in `SearchDisplayController`. After deleting it in storyboard everything works fine – BadCodeDeveloper Jan 13 '17 at 04:25
0

I'm not sure why but the problem was in SearchDisplayController. After deleting it in storyboard everything works fine

BadCodeDeveloper
  • 455
  • 5
  • 24