I added a search bar and a search display controller from the interface designer to my application. I cannot get it to deinoc correctly.
The following behavior is shown (swift2, ios9):
- The user is not looking for anything, just selects an item from tableView, DEINIT is called
- The user is looking for something (or just brief in the search bar), cancel the search, select an item from the View table, DEINIT is called
- The user searches for something (or simply selects the search string), and selects an element from tableView, DEINIT is NOT called : (
The same behavior occurs if I select "Back" in the navigation controller instead of selecting an item.
code removed - refer to COMPLETE CODE at bottom of post.
Any help appreciated!
UPDATE . Further testing showed that removing progressHud / loadHud from the view controller does not completely affect the DEINIT failure caused. This should be somehow related to the table view or the search controller itself ...
UPDATE 2 I tried calling the searchBarCancelButtonClicked () method in my WillDissapear view, and it is still not free. Despite the fact that if you click cancel, then release it, ...
UPDATE 3 Changing the willDisappear / didDisappear parameter to the following did not affect DEINIT - but it does not give an error problem (thanks to Pauline). I'm trying to do my best to get a release, but so far no luck.
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(true)
searchBarCancelButtonClicked(searchController.searchBar)
}
override func viewDidDisappear(animated: Bool) {
print("View did disappear")
searchController.searchBar.resignFirstResponder()
searchController.searchBar.endEditing(true)
searchController.active = false
loadingHud.removeFromSuperview()
progressHud.removeFromSuperview()
searchController.searchBar.delegate = nil
searchController.searchResultsUpdater = nil
searchController = nil
tableView = nil
super.viewDidDisappear(true)
}
UPDATE 4 I did not find an answer. Really hoping someone can help!
5 @ConfusedByCode - [unowned self] in
:
code removed - refer to COMPLETE CODE at bottom of post
DEINIT. , -.
6 , [weak self] in
. DEINIT .
7 appDel unowned let appDel
searchBar.resignFirstResponder() finishSearch(). deinit.
: ( 7)
,
class AirportSearchTBVC: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
var airportData = [Dictionary<String, String>]()
var filteredData = [Dictionary<String, String>]()
var searchController: UISearchController!
unowned let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
var progressHud: ProgressHUD!
var loadingHud: ProgressHUD!
var arrDepOfFlight: String!
var dateOfFlight: NSDate!
var tailNum: String!
var selectedAirportIdent: String!
deinit {
print("TBVC Dealloc")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
progressHud = ProgressHUD(text: "Searching")
loadingHud = ProgressHUD(text: "Loading")
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
tableView.tableHeaderView = searchController.searchBar
definesPresentationContext = true
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.delegate = self
view.addSubview(loadingHud)
appDel.backgroundThread(background: { [weak self] in
if let weakSelf = self {
let airportHelper = AirportHelper()
weakSelf.airportData = airportHelper.getAirportSearchData()
}
},
completion: {
dispatch_async(dispatch_get_main_queue()) { [weak self] in
if let weakSelf = self {
if weakSelf.isVisible && weakSelf.isTopViewController {
weakSelf.filteredData = (weakSelf.airportData)
weakSelf.loadingHud.removeFromSuperview()
weakSelf.updateSearchResultsForSearchController(weakSelf.searchController)
weakSelf.tableView.reloadData()
}
}
}
});
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchController.searchBar.endEditing(true)
searchController.searchBar.resignFirstResponder()
filteredData = airportData
tableView.reloadData()
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
if isVisible && isTopViewController {
if let startCount = searchController.searchBar.text?.length {
if searchController.searchBar.text!.length >= 3 && searchController.searchBar.text!.length == startCount{
view.addSubview(progressHud)
finishSearch()
}
}
}
}
func finishSearch () {
appDel.backgroundThread(background: { [weak self] in
if let weakSelf = self {
if weakSelf.isVisible && weakSelf.isTopViewController {
let searchText = weakSelf.searchController.searchBar.text!.lowercaseString
weakSelf.searchController.searchBar.resignFirstResponder()
weakSelf.filteredData = weakSelf.airportData.filter{
if let ident = $0["ident"] {
if ident.lowercaseString.rangeOfString(searchText) != nil {
return true
}
}
if let name = $0["name"] {
if name.lowercaseString.rangeOfString(searchText) != nil {
return true
}
}
if let city = $0["municipality"] {
if city.lowercaseString.rangeOfString(searchText) != nil {
return true
}
}
return false
}
}
}
},
completion: {
dispatch_async(dispatch_get_main_queue()) { [weak self] in
if let weakSelf = self {
if weakSelf.isVisible && weakSelf.isTopViewController {
weakSelf.tableView.reloadData()
weakSelf.progressHud.removeFromSuperview()
}
}
}
});
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
if isVisible && isTopViewController {
if let startCount = searchController.searchBar.text?.length {
if searchController.searchBar.text!.length >= 3 && searchController.searchBar.text!.length == startCount{
view.addSubview(progressHud)
finishSearch()
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 72
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.active {
return filteredData.count
} else {
return airportData.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: AirportSearchTableViewCell
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! AirportSearchTableViewCell
if searchController.active {
let airportDict = filteredData[indexPath.row]
let airportIdent = airportDict["ident"]
let airportName = airportDict["name"]
let airportRegion = airportDict["iso_region"]
let airportCity = airportDict["municipality"]
cell.loadItem(airportIdent, name: airportName, region: airportRegion, city: airportCity)
} else {
let airportDict = airportData[indexPath.row]
let airportIdent = airportDict["ident"]
let airportName = airportDict["name"]
let airportRegion = airportDict["iso_region"]
let airportCity = airportDict["municipality"]
cell.loadItem(airportIdent, name: airportName, region: airportRegion, city: airportCity)
}
return cell
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(true)
searchBarCancelButtonClicked(searchController.searchBar)
}
override func viewDidDisappear(animated: Bool) {
print("View did disappear")
searchController.searchBar.resignFirstResponder()
searchController.searchBar.endEditing(true)
searchController.active = false
searchController.delegate = nil
searchController.resignFirstResponder()
loadingHud.removeFromSuperview()
progressHud.removeFromSuperview()
searchController.searchBar.delegate = nil
searchController.searchResultsUpdater = nil
searchController.removeFromParentViewController()
searchController = nil
tableView = nil
super.viewDidDisappear(true)
}
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! AirportSearchTableViewCell
selectedAirportIdent = cell.identLbl.text!
self.performSegueWithIdentifier("searchMapVC", sender: nil)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "searchMapVC" {
let mapVC = segue.destinationViewController as! SearchMapController
mapVC.arrDepOfFlight = arrDepOfFlight
mapVC.dateOfFlight = dateOfFlight
mapVC.tailNum = tailNum
mapVC.selectedAirportIdent = selectedAirportIdent
}
}
}
extension String {
var length: Int { return characters.count }
}
extension UIViewController {
public var isVisible: Bool {
if isViewLoaded() {
return view.window != nil
}
return false
}
public var isTopViewController: Bool {
if self.navigationController != nil {
return self.navigationController?.visibleViewController === self
} else if self.tabBarController != nil {
return self.tabBarController?.selectedViewController == self && self.presentedViewController == nil
} else {
return self.presentedViewController == nil && self.isVisible
}
}
}
[]. , , definePresentationContext, viewWillAppear. . .
! , @confusedByCode - , , .
import UIKit
class AirportSearchTBVC: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
var airportData = [Dictionary<String, String>]()
var filteredData = [Dictionary<String, String>]()
var searchController: UISearchController!
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
var progressHud: ProgressHUD!
var loadingHud: ProgressHUD!
var arrDepOfFlight: String!
var dateOfFlight: NSDate!
var tailNum: String!
var selectedAirportIdent: String!
deinit {
print("TBVC Dealloc")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
progressHud = ProgressHUD(text: "Searching")
loadingHud = ProgressHUD(text: "Loading")
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
tableView.tableHeaderView = searchController.searchBar
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.delegate = self
view.addSubview(loadingHud)
appDel.backgroundThread(background: { [weak self] in
if let weakSelf = self {
let airportHelper = AirportHelper()
weakSelf.airportData = airportHelper.getAirportSearchData()
}
},
completion: {
dispatch_async(dispatch_get_main_queue()) { [weak self] in
if let weakSelf = self {
print("isVisible: \(weakSelf.isVisible)")
print("isTopViewController: \(weakSelf.isTopViewController)")
if weakSelf.isVisible {
weakSelf.filteredData = (weakSelf.airportData)
weakSelf.loadingHud.removeFromSuperview()
weakSelf.updateSearchResultsForSearchController(weakSelf.searchController)
weakSelf.tableView.reloadData()
}
}
}
});
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchController.searchBar.endEditing(true)
searchController.searchBar.resignFirstResponder()
filteredData = airportData
tableView.reloadData()
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
if let startCount = searchController.searchBar.text?.length {
if searchController.searchBar.text!.length >= 3 && searchController.searchBar.text!.length == startCount{
view.addSubview(progressHud)
finishSearch()
}
}
}
func finishSearch () {
appDel.backgroundThread(background: { [weak self] in
if let weakSelf = self {
let searchText = weakSelf.searchController.searchBar.text!.lowercaseString
weakSelf.filteredData = weakSelf.airportData.filter{
if let ident = $0["ident"] {
if ident.lowercaseString.rangeOfString(searchText) != nil {
return true
}
}
if let name = $0["name"] {
if name.lowercaseString.rangeOfString(searchText) != nil {
return true
}
}
if let city = $0["municipality"] {
if city.lowercaseString.rangeOfString(searchText) != nil {
return true
}
}
return false
}
}
},
completion: {
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
if self.isVisible {
self.tableView.reloadData()
self.progressHud.removeFromSuperview()
}
}
});
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
if let startCount = searchController.searchBar.text?.length {
if searchController.searchBar.text!.length >= 3 && searchController.searchBar.text!.length == startCount{
view.addSubview(progressHud)
finishSearch()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 72
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.active {
return filteredData.count
} else {
return airportData.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: AirportSearchTableViewCell
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! AirportSearchTableViewCell
if searchController.active {
let airportDict = filteredData[indexPath.row]
let airportIdent = airportDict["ident"]
let airportName = airportDict["name"]
let airportRegion = airportDict["iso_region"]
let airportCity = airportDict["municipality"]
cell.loadItem(airportIdent, name: airportName, region: airportRegion, city: airportCity)
} else {
let airportDict = airportData[indexPath.row]
let airportIdent = airportDict["ident"]
let airportName = airportDict["name"]
let airportRegion = airportDict["iso_region"]
let airportCity = airportDict["municipality"]
cell.loadItem(airportIdent, name: airportName, region: airportRegion, city: airportCity)
}
return cell
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(true)
searchController.active = false
loadingHud.removeFromSuperview()
progressHud.removeFromSuperview()
}
override func viewDidDisappear(animated: Bool) {
print("View did disappear")
super.viewDidDisappear(true)
}
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! AirportSearchTableViewCell
selectedAirportIdent = cell.identLbl.text!
self.performSegueWithIdentifier("searchMapVC", sender: nil)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "searchMapVC" {
let mapVC = segue.destinationViewController as! SearchMapController
mapVC.arrDepOfFlight = arrDepOfFlight
mapVC.dateOfFlight = dateOfFlight
mapVC.tailNum = tailNum
mapVC.selectedAirportIdent = selectedAirportIdent
}
}
}
extension String {
var length: Int { return characters.count }
}
extension UIViewController {
public var isVisible: Bool {
if isViewLoaded() {
return view.window != nil
}
return false
}
public var isTopViewController: Bool {
if self.navigationController != nil {
return self.navigationController?.visibleViewController === self
} else if self.tabBarController != nil {
return self.tabBarController?.selectedViewController == self && self.presentedViewController == nil
} else {
return self.presentedViewController == nil && self.isVisible
}
}
}