Changing UITableViewController to UITableView crashes app - ios

I have just changed from a UITableViewController to a UITableView, and everything is working fine, with the exception of the search bar, which when tapping on a key on the keyboard will crash the app with the error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier rideCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
I haven't changed anything in terms of the code. Someone suggested changing:
let cell = tableView.dequeueReusableCellWithIdentifier("rideCell", forIndexPath: indexPath) as! RideCell
to:
let cell = tableView.dequeueReusableCellWithIdentifier("rideCell") as! RideCell
but that didn't help at all.
Any suggestions?
EDIT:
Here is my code as requested:
class InitalViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var rideName = ""
var parkPassed: Park!
var searchResults = NSArray()
var loadingIndicator = UIActivityIndicatorView()
var backgroundLabel = LabelWithInsets()
var refreshSpinner = UIRefreshControl()
func handleRefresh(refreshControl: UIRefreshControl) {
DataManager.sharedInstance.loadRides(parkPassed.name!.stringByReplacingOccurrencesOfString(" ", withString: ""))
refreshControl.endRefreshing()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
backgroundLabel.textAlignment = NSTextAlignment.Center
backgroundLabel.textColor = UIColorFromRGB(0x424242)
backgroundLabel.numberOfLines = 0
refreshSpinner.addTarget(self, action: "handleRefresh:", forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshSpinner)
//refreshControl = refreshSpinner
self.tableView.tableHeaderView = searchBar
self.tableView.contentOffset = CGPointMake(0, CGRectGetHeight(searchBar.frame))
self.navigationController?.navigationBar.barTintColor = UIColorFromRGB(0x0096FF)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateTableView", name: "onRidesLoadedNotification", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "displayError", name: "onRidesLoadFailedNotification", object: nil)
}
override func viewWillAppear(animated: Bool) {
if DataManager.sharedInstance.rideArray.count == 0 {
tableView.separatorStyle = UITableViewCellSeparatorStyle.None
loadingIndicator.frame = CGRectMake((view.bounds.width / 2) - 25, (view.bounds.height / 2) - 50, 50, 50)
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
view.addSubview(loadingIndicator)
loadingIndicator.startAnimating()
backgroundLabel.text = "Loading rides..."
backgroundLabel.frame = CGRectMake(0, (view.bounds.height / 2) - 80, view.bounds.width, 30)
view.addSubview(backgroundLabel)
}
DataManager.sharedInstance.loadRides(parkPassed.name!.stringByReplacingOccurrencesOfString(" ", withString: ""))
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: false)
if NSUserDefaults.standardUserDefaults().arrayForKey("favourites") != nil {
let tempFavourites: NSArray = NSUserDefaults.standardUserDefaults().arrayForKey("favourites")!
favouritesArray = tempFavourites.mutableCopy() as! NSMutableArray
}
}
#IBAction func dismiss(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
func updateTableView() {
tableView.reloadData()
tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine
loadingIndicator.removeFromSuperview()
backgroundLabel.removeFromSuperview()
}
func displayError() {
loadingIndicator.removeFromSuperview()
backgroundLabel.removeFromSuperview()
backgroundLabel.text = "Failed to load rides. Please check your internet connection."
backgroundLabel.frame = CGRectMake(0, (view.bounds.height / 2) - 80, view.bounds.width, 60)
view.addSubview(backgroundLabel)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(animated: Bool) {
println(DataManager.sharedInstance.rideArray.count)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("rideCell", forIndexPath: indexPath) as! RideCell
var ride: Ride
if tableView == self.searchDisplayController?.searchResultsTableView {
ride = DataManager.sharedInstance.getRideByName(searchResults[indexPath.row].name)!
} else {
ride = DataManager.sharedInstance.rideAtLocation(indexPath.row)!
}
cell.rideNameLabel.text = ride.name
var dateSinceUpdate = NSDate().timeIntervalSinceDate(ride.updated!)
var secondsSinceUpdate = Int(dateSinceUpdate)
var timeSinceUpdate = printSecondsConvert(secondsSinceUpdate)
cell.updatedLabel.text = timeSinceUpdate
if ride.waitTime == "Closed" {
cell.waitTimeLabel.text = ride.waitTime!
cell.timeBackgroundView.backgroundColor = getColorFromNumber(80)
cell.waitTimeLabel.font = UIFont(name: "Avenir", size: 13)
} else {
cell.waitTimeLabel.text = "\(ride.waitTime!)m"
cell.timeBackgroundView.backgroundColor = getColorFromNumber(ride.waitTime!.toInt()!)
cell.waitTimeLabel.font = UIFont(name: "Avenir", size: 17)
}
AsyncImageLoader.sharedLoader().cancelLoadingURL(cell.rideImageView.imageURL)
cell.rideImageView.image = UIImage(named: "Unloaded")
cell.rideImageView.imageURL = NSURL(string: ride.rideImageSmall!)
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.searchDisplayController?.searchResultsTableView {
return searchResults.count
} else {
return DataManager.sharedInstance.rideArray.count
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as! RideCell!
rideName = currentCell.rideNameLabel.text!
performSegueWithIdentifier("showDetail", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showDetail") {
var viewController = segue.destinationViewController as! DetailViewController
viewController.currentRide = DataManager.sharedInstance.getRideByName(rideName)
viewController.parkPassed = parkPassed
}
}
func filterContentForSearchText(searchText: NSString) {
let resultPredicate = NSPredicate(format: "name contains[cd] %#", searchText) //Use either contains or beginswith
searchResults = (DataManager.sharedInstance.rideArray as NSArray).filteredArrayUsingPredicate(resultPredicate)
}
func searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchString searchString: String!) -> Bool {
self.filterContentForSearchText(searchString)
return true
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 71
}
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {
var cell = tableView.cellForRowAtIndexPath(indexPath) as! RideCell
let favourite = UITableViewRowAction(style: .Normal, title: " ") { action, index in
if favouritesArray.containsObject(cell.rideNameLabel.text!) {
favouritesArray.removeObject(cell.rideNameLabel.text!)
} else {
favouritesArray.addObject(cell.rideNameLabel.text!)
}
NSUserDefaults.standardUserDefaults().setObject(favouritesArray, forKey: "favourites")
tableView.setEditing(false, animated: true)
}
if favouritesArray.containsObject(cell.rideNameLabel.text!) {
favourite.backgroundColor = UIColor(patternImage: UIImage(named: "Unfavourite")!)
} else {
favourite.backgroundColor = UIColor(patternImage: UIImage(named: "Favourite")!)
}
return [favourite]
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
}
}

Call next code in viewDidLoad method of view controller that contains your table view:
If you created RideCell in xib:
tableView.registerNib(UINib(nibName: "RideCell", bundle: nil),
forCellReuseIdentifier: "rideCell")
If you created RideCell in code:
tableView.registerClass(RideCell.self, forCellReuseIdentifier: "rideCell")

In your viewDidLoad you have to register the custom cell to your UITableView using this function:
func registerNib(_ nib: UINib,forCellReuseIdentifier identifier: String)

Related

UISearchController cancel button dismisses UITabBarController when tapped multiple times in a row

I use a custom search controller that has a tableView for showing the results,
the problem is when tapping the cancel button multiple times it dismisses the tabBarController.
But if i press it normally it acts as it should be.
class UICustomSearchController: UISearchController {
private var suggestedTableView: UITableView!
weak var suggestionDelegate: SearchSuggestionsDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
suggestedTableView = UITableView(frame: CGRect(x: 0, y: searchBar.frame.maxY,
width: view.frame.width,
height: view.frame.height - 70))
suggestedTableView.backgroundColor = UIColor.clear
suggestedTableView.tableFooterView = UIView()
view.subviews.forEach {
if $0.isKind(of: UITableView.self) {
$0.removeFromSuperview()
}
}
view.addSubview(suggestedTableView)
suggestedTableView.dataSource = self
suggestedTableView.delegate = self
suggestedTableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
let tap = UITapGestureRecognizer(target: self, action: #selector(tableTapped))
suggestedTableView.addGestureRecognizer(tap)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
suggestedTableView.removeFromSuperview()
suggestedTableView = nil
dismiss(animated: false, completion: nil)
}
func reloadSuggestions() {
suggestedTableView.reloadData()
}
private func dismissView() {
searchBar.text = ""
suggestedTableView.removeFromSuperview()
dismiss(animated: false, completion: nil)
suggestionDelegate?.didDismissed()
}
// MARK: - Actions
#objc func tableTapped(tap:UITapGestureRecognizer) {
suggestedTableView.removeGestureRecognizer(tap)
let location = tap.location(in: suggestedTableView)
let path = suggestedTableView.indexPathForRow(at: location)
if let indexPathForRow = path {
tableView(suggestedTableView, didSelectRowAt: indexPathForRow)
} else {
dismissView()
}
}
}
extension UICustomSearchController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return suggestionDelegate?.suggestions().count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let list = suggestionDelegate?.suggestions() ?? []
cell.textLabel?.text = list[indexPath.row]
cell.textLabel?.textColor = UIColor.color(from: .blueTabBar)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let list = suggestionDelegate?.suggestions() ?? []
searchBar.text = list[indexPath.row]
searchBar.becomeFirstResponder()
suggestionDelegate?.didSelect(suggestion: list[indexPath.row])
}
}
And in the viewController that has search:
func initSearchViews() {
// Create the search controller and specify that it should present its results in this same view
searchController = UICustomSearchController(searchResultsController: nil)
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.barTintColor = UIColor.white
searchController.searchBar.tintColor = UIColor.color(from: .blueTabBar)
searchController.searchBar.setValue(Strings.cancel.localized, forKey:"_cancelButtonText")
searchController.suggestionDelegate = self
if let searchTextField = searchController!.searchBar.value(forKey: "searchField") as? UITextField {
searchTextField.placeholder = Strings.search.localized
}
// Make this class the delegate and present the search
searchController.searchBar.delegate = self
}
I tried putting this line in viewController but nothing happened:
definesPresentationContext = true

DidSelectRow method not called

Hi guys I have been trying for few days no answer found . I have already implemented UITableViewDelegate and UITableViewDataSource before raising this question my didSelectRowAt and didDeselectRowAt, both the methods are not working.
class SearchClass: UIViewController, UITableViewDataSource,UITableViewDelegate, UISearchBarDelegate {
#IBOutlet weak var myTableView: UITableView!
#IBOutlet weak var mySearchBar: UISearchBar!
var objects:PFObject!
var searchResults = [String]()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.navigationBar.topItem?.title = "Search"
self.navigationController?.navigationBar.barTintColor = UIColor.white
self.navigationController?.navigationBar.backgroundColor = UIColor.black
self.navigationController?.navigationBar.tintColor = UIColor.black
self.searchResults.removeAll()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.myTableView.dataSource = self
self.mySearchBar.delegate = self
self.myTableView.delegate = self
self.navigationController?.extendedLayoutIncludesOpaqueBars = true
//self.myTableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
{
searchBar.resignFirstResponder()
// self.navigationController?.navigationBar.isHidden = false
self.mySearchBar.endEditing(true)
print("Search word = \(searchBar.text)")
let query = PFQuery(className:"myClass")
//let newText = searchBar.autocapitalization
searchBar.autocapitalizationType = .none
searchBar.text = searchBar.text?.localizedLowercase
query.whereKey("collegeNickName", contains: searchBar.text)
query.findObjectsInBackground { (results, error) in
if error == nil {
if let objects = results {
self.searchResults.removeAll(keepingCapacity: true)
for object in objects {
let firstName = object.object(forKey: "myName") as! String
let image = object.object(forKey: "myImage") as! PFFile
// let lastName = object.object(forKey: "myPlace") as! String
// let fullName = firstName + " " + lastName
self.searchResults.append(firstName)
print(self.searchResults[0])
DispatchQueue.main.async {
self.myTableView.reloadData()
self.mySearchBar.resignFirstResponder()
}
}
}
} else {
let myAlert = UIAlertController(title:"Alert", message:error?.localizedDescription, preferredStyle:UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
return
}
}
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
//self.navigationController?.navigationBar.isHidden = true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return searchResults.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
let myCell = myTableView.dequeueReusableCell(withIdentifier: "myCell")
myCell?.textLabel?.text = searchResults[indexPath.row]
print(searchResults[indexPath.row])
return myCell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Hi")
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar)
{
mySearchBar.resignFirstResponder()
mySearchBar.text = ""
myTableView.reloadData()
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
//self.mySearchBar.resignFirstResponder()
//self.mySearchBar.endEditing(true)
self.definesPresentationContext = true
}
#IBAction func refreshButtonTapped(sender: AnyObject) {
mySearchBar.resignFirstResponder()
mySearchBar.text = ""
self.searchResults.removeAll(keepingCapacity: false)
self.myTableView.reloadData()
}
}
Also it implements a searchView I'm getting what I want to search but unable use select and deselect methods in my class.
self.myTableView.dataSource = self
self.myTableView.delegate = self
<--- add this.
Noticed that you've set delegate in the storyboard as view instead of your UIViewController
see Discover - that's mine UViewController
In Interface Builder
Connect dataSource and delegate of My Table View to SearchClass
Then you can delete redundant self.myTableView.dataSource = self in SearchClass
Consider that connections in Interface Builder are more efficient than in code.
Declaration of cell is wrong. you have done this below code
let myCell = myTableView.dequeueReusableCell(withIdentifier: "myCell")
Right format
let myCell = tableView.dequeueReusableCell(withIdentifier: "myCell")
Reason: When you use dequeue property, then UITableView dequeue its cell by the param it got from the method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
see there is a param tableView but you are dequeing by the outllet of UITableView.

Getting Crash in cellForRowAtIndexPath in tableView on Crashlytics

I got weird crash on Crashlytics, It's showing crash in the line as commented in code.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row >= collectionArrayApiObject.collectionsArray.count {
return UITableViewCell(frame: CGRectZero)
}
var tableViewCell:CollectionsTableViewCell? = tableView.dequeueReusableCellWithIdentifier("collectionsCell") as? CollectionsTableViewCell
if (tableViewCell == nil) {
tableViewCell = CollectionsTableViewCell(style: .Default, reuseIdentifier: "collectionsCell")
}
// Got crash in below line
let collectionObject = collectionArrayApiObject.collectionsArray[indexPath.row]
tableViewCell!.setCollection(collectionObject)
return tableViewCell!
}
This is the stack trace that I got..
But not getting why this even happened? I have already put check that
index.row >= collectionArrayApiObject.collectionsArray.count
so it should not be index out of range case. Can any one give me just one case in which this can happen?
NOTE: This happened with only one user till now but still why this even happened?
Whole Table View Controller:
import UIKit
class CollectionsViewController: CUIBaseViewController, CollectionArrayApiObjectDelegate, UITableViewDataSource, UITableViewDelegate, UIViewControllerPreviewingDelegate {
var collectionsTableView : UITableView?
var collectionArrayApiObject : CollectionArrayApiObject = CollectionArrayApiObject()
var headerSearchButton : UIBarButtonItem?
var cityId: String?
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
convenience init(CityId: String? = nil) {
self.init(nibName: nil, bundle: nil)
self.cityId = CityId
self.view.backgroundColor = Colors.white()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.setUpHeaderView()
self.createTableView()
self.getData()
self.listenToNotifications()
self.automaticallyAdjustsScrollViewInsets = false
if #available(iOS 9.0, *) {
registerForPreviewingWithDelegate(self, sourceView: collectionsTableView!)
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
}
func listenToNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(CollectionsViewController.cityChanged), name: Constants.NOTIFIICATION_LOCATION_CHANGED_CITY, object: nil)
}
func cityChanged() {
self.getData()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionsTableView?.frame = CGRectMake(0, Constants.NAV_BAR_HEIGHT, self.view.width, self.view.height - Constants.NAV_BAR_HEIGHT)
}
//MARK: view creation functions
func setUpHeaderView() {
self.navigationItem.title = "Collections"
let negativeSpacer = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil)
negativeSpacer.width = 0;
headerSearchButton = UIBarButtonItem.init(title: "n", style: .Plain, target: self, action: #selector(CollectionsViewController.headerSearchButtonTapped))
headerSearchButton?.width = 40
headerSearchButton?.setTitleTextAttributes([NSFontAttributeName: Fonts.iconFont(18), NSForegroundColorAttributeName: Colors.gray()], forState: .Normal)
self.navigationItem.rightBarButtonItems = [negativeSpacer, headerSearchButton!];
}
func createTableView() {
collectionsTableView = UITableView(frame: CGRectZero, style: .Plain)
collectionsTableView?.separatorStyle = .None
collectionsTableView?.backgroundColor = Colors.white()
collectionsTableView?.dataSource = self
collectionsTableView?.delegate = self
self.view.addSubview(collectionsTableView!)
}
//MARK : tableview delegate methods
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return collectionArrayApiObject.collectionsArray.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return CollectionsTableViewCell.getHeight()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row >= collectionArrayApiObject.collectionsArray.count {
return UITableViewCell(frame: CGRectZero)
}
var tableViewCell:CollectionsTableViewCell? = tableView.dequeueReusableCellWithIdentifier("collectionsCell") as? CollectionsTableViewCell
if (tableViewCell == nil) {
tableViewCell = CollectionsTableViewCell(style: .Default, reuseIdentifier: "collectionsCell")
}
let collectionObject = collectionArrayApiObject.collectionsArray[indexPath.row]
tableViewCell!.setCollection(collectionObject)
return tableViewCell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
Commons.trackEvent(Commons.createGAcategory([Constants.COLLECTIONS_PAGE, "collection"]), action: "click", label: nil, value: 1)
let exploreFeedForCollection = ExploreFeedViewController.init(collection: collectionArrayApiObject.collectionsArray[indexPath.row])
//self.presentViewController(CUINavigationController.init(rootViewController: exploreFeedForCollection), animated: true, completion: nil)
self.navigationController?.pushViewController(exploreFeedForCollection, animated: true)
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let contentOffset = scrollView.contentOffset.y
if (collectionsTableView != nil) {
for cell in collectionsTableView!.visibleCells {
if (cell.isKindOfClass(CollectionsTableViewCell.self)) {
let cellOffset = cell.y - contentOffset;
//image parallax
let parallaxCut: CGFloat = 0.5
let percent = (cellOffset + cell.height)/(collectionsTableView!.height + cell.height);
let extraHeight = cell.height * (CollectionsTableViewCell.parallaxRatio-1.0) * parallaxCut;
let collectionCell = cell as! CollectionsTableViewCell;
collectionCell.bgImageView.y = -extraHeight*percent;
}
}
}
}
//MARK : get cities data functions
func getData() {
collectionArrayApiObject = CollectionArrayApiObject()
collectionArrayApiObject.fetchCollections(Delegate: self, CityId: self.cityId)
if collectionArrayApiObject.collectionsArray.count == 0 {
self.showLoader("Hmm, things are getting interesting")
}
}
func collectionsFetchedSuccessfully() {
self.hideNothingHereViewAndLoader()
self.collectionsTableView?.reloadData()
self.scrollViewDidScroll(self.collectionsTableView!)
}
func collectionsFetchingFailed(errorType: ErrorType) {
self.showNothingHereView(errorType, icon: nil, showTryAgain: true)
}
override func didTapReloadButton() {
self.getData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: UIViewControllerPreviewingDelegate
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
guard let indexPath = collectionsTableView?.indexPathForRowAtPoint(location) else{
return nil
}
// Lifting cell up on 3D touch before peeking
if #available(iOS 9.0, *) {
let cellRect = collectionsTableView?.rectForRowAtIndexPath(indexPath)
let sourceRect = previewingContext.sourceView.convertRect(cellRect!, fromView: collectionsTableView)
previewingContext.sourceRect = sourceRect
}
return ExploreFeedViewController.init(collection: collectionArrayApiObject.collectionsArray[(indexPath as NSIndexPath).row])
}
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) {
self.navigationController?.pushViewController(viewControllerToCommit, animated: true)
}
//MARK : header button functions
func headerSearchButtonTapped() {
let searchVC = SearchViewController(CityId: cityId)
self.navigationController?.pushViewController(searchVC, animated: true)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
Some observations:
First
Instead of using this if statement:
if indexPath.row >= collectionArrayApiObject.collectionsArray.count {
return UITableViewCell(frame: CGRectZero)
}
You should use the numberOfRowsInSection to do that, like this:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return collectionArrayApiObject.collectionsArray.count
}
Second
In your cellForRowAtindexPath you just have to dequeue the cell and than set it as you need, like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//swift 3.0
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CollectionsTableViewCell
//swift 2.2
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CollectionsTableViewCell
let collectionObject = collectionArrayApiObject.collectionsArray[indexPath.row]
cell.setCollection(collectionObject)
return cell
}
There isn't need to test if you use dequeueReusableCellWithIdentifier:forIndexPath:) which according to the docs always returns a valid cell
1- if you are using like this
let cell = tableChatHome.dequeueReusableCell(withIdentifier: "tableChatHomeCell1", for: indexPath) as! tableChatHomeCell1
2- change to like this work fine for me
let cell = tableView.dequeueReusableCell(withIdentifier: "tableChatHomeCell1") as! tableChatHomeCell1

Delegate Isn't getting Created in Swift

I am working on a swift project involving delegates and protocols. I have two files that are working smoothly in this, however I am having an issue where the delegate keeps coming up nil in a different two files where I am doing the same thing as before with different names. It seems like the delegate isn't being set/created for some reason. See code below:
ItemDetailViewController
import UIKit
protocol ItemDetailViewControllerDelegate: class {
func itemDetailViewControllerDidCancel(controller: ItemDetailViewController)
func itemDetailViewController(controller: ItemDetailViewController, didFinishAddingItem item: ChecklistItem)
func itemDetailViewController(controller: ItemDetailViewController, didFinishEditingItem item: ChecklistItem)
}
class ItemDetailViewController: UITableViewController, UITextFieldDelegate {
**weak var delegate: ItemDetailViewControllerDelegate?**
#IBOutlet weak var doneBarButton: UIBarButtonItem!
#IBOutlet weak var textField: UITextField!
var itemToEdit: ChecklistItem?
#IBAction func cancel() {
delegate?.itemDetailViewControllerDidCancel(self)
}
#IBAction func done() {
if let item = itemToEdit {
item.text = textField.text!
delegate?.itemDetailViewController(self, didFinishEditingItem: item)
}
else {
let item = ChecklistItem()
item.text = textField.text!
delegate?.itemDetailViewController(self, didFinishAddingItem: item)
}
}
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
return nil
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
textField.becomeFirstResponder()
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let oldText: NSString = textField.text!
let newText: NSString = oldText.stringByReplacingCharactersInRange(range, withString: string)
doneBarButton.enabled = (newText.length > 0)
return true
}
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
if let item = itemToEdit {
title = "Edit Item"
textField.text = item.text
doneBarButton.enabled = true
}
}
}
ChecklistViewController
import UIKit
class ChecklistViewController: UITableViewController, ItemDetailViewControllerDelegate {
var checklist: Checklist!
override func viewDidLoad() {
super.viewDidLoad()
title = checklist.name
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return checklist.items.count
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let item = checklist.items[indexPath.row]
performSegueWithIdentifier("ShowMap", sender: item)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ChecklistItem", forIndexPath: indexPath)
let item = checklist.items[indexPath.row]
configureTextForCell(cell, withChecklistItem: item)
return cell
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
// 1
checklist.items.removeAtIndex(indexPath.row)
// 2
let indexPaths = [indexPath]
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
}
func configureTextForCell(cell: UITableViewCell,withChecklistItem item: ChecklistItem) {
let label = cell.viewWithTag(1000) as! UILabel
label.text = item.text
}
func itemDetailViewControllerDidCancel(controller: ItemDetailViewController) {
dismissViewControllerAnimated(true, completion: nil)
}
func itemDetailViewController(controller: ItemDetailViewController,didFinishEditingItem item: ChecklistItem) {
if let index = checklist.items.indexOf(item) { // indexOf needs to compare (test for equality) item to the items in the array
let indexPath = NSIndexPath(forRow: index, inSection: 0)
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
configureTextForCell(cell, withChecklistItem: item)
}
}
dismissViewControllerAnimated(true, completion: nil)
}
func itemDetailViewController(controller: ItemDetailViewController, didFinishAddingItem item: ChecklistItem) {
let newRowIndex = checklist.items.count
checklist.items.append(item)
let indexPath = NSIndexPath(forRow: newRowIndex, inSection: 0)
let indexPaths = [indexPath]
tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
dismissViewControllerAnimated(true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue,sender: AnyObject?) {
if segue.identifier == "AddItem" {
let navigationController = segue.destinationViewController
as! UINavigationController
let controller = navigationController.topViewController
as! ItemDetailViewController
controller.delegate = self
}
else if segue.identifier == "EditItem" {
let navigationController = segue.destinationViewController
as! UINavigationController
let controller = navigationController.topViewController
as! ItemDetailViewController
controller.delegate = self
if let indexPath = tableView.indexPathForCell(
sender as! UITableViewCell) {
controller.itemToEdit = checklist.items[indexPath.row]
}
}
}
}
Is it possible that your segue identifiers in ChecklistViewController:prepareForSegue are not correct, or are not setup correctly in the storyboard? If so the destination controller delegate would not get set.

Are there some optional wrong here?

This is the code and the problem
There is no wrong when I did select one row before I add the function tableView(......didSelectRowatindexPath...)
So, I thought it's the root cause.
I hope somebody can help me because the wrong info was not so clear that I can understand it well.
What I want to do is change the BarItemName when I did select one row of my popover table.
SwitchA is a var in my popoverviewcontroller, it means which button is pressed.
When the button in "SecondVC" is pressed,it will pass a value to SwitchA and then the popoverviewcontroller can determine which datasource it should show.
PS:this is the popoverviewcontroller's code.
import UIKit
class PopOverView: UIViewController, UITableViewDataSource, UITableViewDelegate {
var SwitchA = 0
var ClassA = ["这个类型","那个类型","这个类型","那个类型","这个类型","那个类型","这个类型","那个类型","这个类型","那个类型"]
var TimeA = ["昨天","今天","明天","昨天","今天","明天"]
var TagA = ["动漫","音乐","游戏","音乐","游戏"]
#IBOutlet weak var TV: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
TV.dataSource = self
TV.delegate = self
self.preferredContentSize = CGSize(width: UIScreen.mainScreen().bounds.width, height: 175)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if SwitchA == 0 {
return ClassA.count
}
if SwitchA == 1 {
return TimeA.count
}
else {
return TagA.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
if SwitchA == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
cell.textLabel?.text = ClassA[indexPath.row]
cell.separatorInset = UIEdgeInsetsZero
cell.preservesSuperviewLayoutMargins = false
cell.layoutMargins = UIEdgeInsetsZero
return cell
}
if SwitchA == 1 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
cell.textLabel?.text = TimeA[indexPath.row]
cell.separatorInset = UIEdgeInsetsZero
cell.preservesSuperviewLayoutMargins = false
cell.layoutMargins = UIEdgeInsetsZero
return cell
}
else {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
cell.textLabel?.text = TagA[indexPath.row]
cell.separatorInset = UIEdgeInsetsZero
cell.preservesSuperviewLayoutMargins = false
cell.layoutMargins = UIEdgeInsetsZero
return cell
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 35.0
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
if SwitchA == 0 {
let VC = self.storyboard!.instantiateViewControllerWithIdentifier("SecondVC") as! XiaoNei_HuoDong
VC.ClassName.title = ClassA[indexPath.row]
}
if SwitchA == 1 {
let VC = self.storyboard!.instantiateViewControllerWithIdentifier("SecondVC") as! XiaoNei_HuoDong
VC.TimeName.title = TimeA[indexPath.row]
}
else {
let VC = self.storyboard!.instantiateViewControllerWithIdentifier("SecondVC") as! XiaoNei_HuoDong
VC.TagName.title = TagA[indexPath.row]
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
And this is the code of "SecondVC":
import UIKit
class XiaoNei_HuoDong: UIViewController,UITableViewDelegate,UITableViewDataSource, UIPopoverPresentationControllerDelegate{
#IBOutlet weak var TagName: UIBarButtonItem!
#IBOutlet weak var TimeName: UIBarButtonItem!
#IBOutlet weak var ClassName: UIBarButtonItem!
#IBOutlet weak var huodongTV: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
huodongTV.dataSource = self
huodongTV.delegate = self
huodongTV.showsVerticalScrollIndicator = false
let options = PullToRefreshOption()
options.backgroundColor = UIColor(red: 239/255, green: 239/255, blue: 244/255, alpha: 1)
options.indicatorColor = UIColor.blackColor()
huodongTV.addPullToRefresh(options: options, refreshCompletion: { [weak self] in
// some code
self!.huodongTV.reloadData()
self!.huodongTV.stopPullToRefresh()
})
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return 3
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 1.0
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 1.0
}
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
self.huodongTV.deselectRowAtIndexPath(indexPath, animated: false)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
if indexPath.row == 0 {
let cell:HuoDong_2 = tableView.dequeueReusableCellWithIdentifier("huodong2") as! HuoDong_2
cell.ClubB1.image = UIImage(named: "test")!
cell.ClubB2.image = UIImage(named: "test")!
cell.ClubS1.image = UIImage(named: "focus")!
cell.ClubS2.image = UIImage(named: "focus")!
cell.Tag1.image = UIImage(named: "更新")!
cell.Tag2.image = UIImage(named: "更新")!
cell.View1.image = UIImage(named: "view")!
cell.View2.image = UIImage(named: "view")!
cell.Newest.image = UIImage(named: "club rank")
return cell
}
else {
let cell:HuoDong = tableView.dequeueReusableCellWithIdentifier("huodong1") as! HuoDong
cell.ClubB1.image = UIImage(named: "test")!
cell.ClubB2.image = UIImage(named: "test")!
cell.ClubS1.image = UIImage(named: "focus")!
cell.ClubS2.image = UIImage(named: "focus")!
cell.Tag1.image = UIImage(named: "更新")!
cell.Tag2.image = UIImage(named: "更新")!
cell.View1.image = UIImage(named: "view")!
cell.View2.image = UIImage(named: "view")!
return cell
}
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath){
cell.layer.transform = CATransform3DMakeScale(0.1, 0.1, 1)
UIView.animateWithDuration(0.25, animations: {
cell.layer.transform = CATransform3DMakeScale(1, 1, 1)
})
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == 0 {
return self.view.frame.width * 240.0 / 400.0
}
else {
return self.view.frame.width * 200.0 / 400.0
}
}
// MARK: - PopOverforsegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Class1"{
let VC = segue.destinationViewController as! PopOverView
VC.SwitchA = 0
VC.modalPresentationStyle = UIModalPresentationStyle.Popover
VC.popoverPresentationController?.delegate = self
}
if segue.identifier == "Time1"{
let VC = segue.destinationViewController as! PopOverView
VC.SwitchA = 1
VC.modalPresentationStyle = UIModalPresentationStyle.Popover
VC.popoverPresentationController?.delegate = self
}
if segue.identifier == "Tag1"{
let VC = segue.destinationViewController as! PopOverView
VC.SwitchA = 2
VC.modalPresentationStyle = UIModalPresentationStyle.Popover
VC.popoverPresentationController?.delegate = self
}
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I think problem is timing of initialize.
Try this:
SecondVC
var tagNameStr = ""
override func viewDidLoad() {
super.viewDidLoad()
TagName.title = tagNameStr // here
...
}
FirstVC
if SwitchA == 0 {
let VC = self.storyboard!.instantiateViewControllerWithIdentifier("SecondVC") as! XiaoNei_HuoDong
VC.tagNameStr = ClassA[indexPath.row]
}
Hope this helps!
UPDATE
This is sample code.
(ViewController -|segue|-> SecondViewController)
class ViewController: UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let vc = segue.destinationViewController as? SecondViewController {
vc.buttonTitle = "IOhYES"
}
}
}
class SecondViewController: UIViewController {
#IBOutlet weak var buttonItem: UIBarButtonItem!
var buttonTitle = ""
override func viewDidLoad() {
super.viewDidLoad()
buttonItem.title = buttonTitle
}
}
Please check IBOutlet connection.

Resources