Getting Crash in cellForRowAtIndexPath in tableView on Crashlytics - ios

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

Related

How to make the deleteTask method work properly?

I am trying to create a ToDoList in swift UI and my deleteTask function is not working properly. I tried a lot of things and none of them worked.
import UIKit
class ViewController: UIViewController {
#IBOutlet var tableView: UITableView!
var toDoTasks = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.title = "SAVED TASKS"
tableView.delegate = self
tableView.dataSource = self
// setup
if !UserDefaults().bool(forKey: "setup")
{
UserDefaults().set(true, forKey: "setup")
UserDefaults().set(0, forKey: "count")
}
updateTasks()
}
func updateTasks()
{
toDoTasks.removeAll()
guard let count = UserDefaults().value(forKey: "count") as? Int else
{
return
}
for x in 0..<count
{
if let task = UserDefaults().value(forKey: "task_\(x+1)") as? String
{
toDoTasks.append(task)
}
}
tableView.reloadData()
}
#IBAction func didTapAdd()
{
let viewController = storyboard?.instantiateViewController(withIdentifier: "entry") as! EntryViewController
viewController.title = "NEW TASK"
viewController.updated =
{
DispatchQueue.main.async {
self.updateTasks()
}
}
navigationController?.pushViewController(viewController, animated: true)
}
}
extension ViewController: UITableViewDelegate
{
// function to select the rows
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let viewController = storyboard?.instantiateViewController(withIdentifier: "task") as! TasksViewController
viewController.title = "NEW TASK"
viewController.task = toDoTasks[indexPath.row]
navigationController?.pushViewController(viewController, animated: true)
}
}
extension ViewController: UITableViewDataSource
{
// function which returns number of tasks
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return toDoTasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = toDoTasks[indexPath.row]
return cell
}
}
import UIKit
class TasksViewController: UIViewController {
#IBOutlet var label : UILabel!
var task : String?
var currentPosition: Int?
override func viewDidLoad() {
super.viewDidLoad()
label.text = task
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Delete", style: .done, target: self, action: #selector(deleteTask))
}
#objc func deleteTask(_ sender: UIBarButtonItem) {
guard let currentPosition = self.currentPosition else {
return
}
let count = UserDefaults.standard.integer(forKey: "count")
UserDefaults.standard.removeObject(forKey: "task_\(currentPosition+1)")
for i in currentPosition+1..<count {
let task = UserDefaults.standard.string(forKey: "task_\(i+1)")
UserDefaults.standard.setValue(task, forKey: "task_\(i)")
}
UserDefaults.standard.setValue(count-1, forKey: "count")
navigationController?.popViewController(animated: true)
}
}
When I press the Delete button, nothing happens. Could you please help me??

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

Random grey bar appears on top of ViewController in iOS (swift)

I am making an app currently and on the main screen where I load multiple items from Parse, I see a weird grey bar. I don't know where this bar came from as it does not show up on the storyboard and I don't know how to fix it. It never was a problem until it randomly recently showed up. Here is the code for that ViewController.
import UIKit
import Parse
import ParseUI
import Kingfisher
class HomeTableViewController: PFQueryTableViewController
{
override func viewDidLoad ()
{
super.viewDidLoad()
setTitle()
self.navigationController?.isNavigationBarHidden = true
self.navigationController?.navigationBar.setHeight(0.0)
// self.tableView.scrollsToTop = true
// self.tableView.scrollToNearestSelectedRow(at: UITableViewScrollPosition.bottom, animated: false)
// self.tableView.scrollToNearestSelectedRow(at: UITableViewScrollPosition.top, animated: true)
}
func setTitle()
{
var parentView = self.parent
while parent != nil
{
if let menu = parentView as? CucuMenuController
{
// menu.setTitleForLabel("Cucus")
menu.setTitleForLabel("")
break
}
parentView = parentView?.parent
}
}
var firstTime = true
var totalArticles = 0
let appDel = UIApplication.shared.delegate as! AppDelegate
required init!(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
setupHomefeed()
}
func setupHomefeed()
{
// This runs before didFinishLoadingWithOptions
self.parseClassName = "Article"
self.pullToRefreshEnabled = true
self.paginationEnabled = true
self.objectsPerPage = 10
self.loadingViewEnabled = true
}
override func objectsDidLoad(_ error: Error?)
{
super.objectsDidLoad(error)
if firstTime
{
firstTime = false
self.loadObjects()
self.tableView.reloadData()
}
else
{
PFObject.pinAll(inBackground: self.objects)
}
}
override func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
override func viewDidAppear(_ animated: Bool)
{
navigationController?.isNavigationBarHidden = false
if PFUser.current() == nil || PFUser.current()!["name"] == nil
{
let viewController = storyboard!.instantiateViewController(withIdentifier: "LoginController")
UIApplication.shared.keyWindow?.rootViewController = viewController
}
else if UserDefaults.standard.bool(forKey: "showDemo")
{
self.performSegue(withIdentifier: "detailSegue", sender: self)
}
else
{
self.loadObjects()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if self.objects!.count < totalArticles && self.objects!.count > 0
{
return self.objects!.count + 1
}
return self.objects!.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
if indexPath.row == self.objects?.count
{
return 70.0
}
else
{
return 221.5
}
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0.0
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0.0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as UITableViewCell!
if indexPath.row == self.objects!.count
{
return tableView.dequeueReusableCell(withIdentifier: "loadCell") as UITableViewCell!
}
if (indexPath.row > self.objects!.count)
{
return cell!
}
let imageView = cell?.viewWithTag(200) as! UIImageView
let titleText = cell?.viewWithTag(101) as! UILabel
let timeLabel = cell?.viewWithTag(102) as! UILabel
let newImage = cell?.viewWithTag(100) as! UIImageView
let diffImage = cell?.viewWithTag(300) as! UIImageView
let scoreLabel = cell?.viewWithTag(301) as! UILabel
let catLabel = cell?.viewWithTag(103) as! UILabel
let article = Article(parseData: self.objects![indexPath.row])
titleText.text = article.title
timeLabel.text = article.duration
catLabel.text = article.category.uppercased()
let hasRead = UserController.hasReadArticle(article.objectId)
newImage.isHidden = hasRead
if hasRead
{
let score = UserController.getScoreForArticle(article.objectId)
scoreLabel.text = NSString(format: "YOUR SCORE: %.0f", score) as String
var performance = "hard"
if score >= article.idealScore { performance = "easy" }
else if score * 2 >= article.idealScore { performance = "medium" }
diffImage.image = UIImage(named: performance + "ScoreTag")
if UserController.getPlayableForArticle(article.objectId) {
cell?.isUserInteractionEnabled = true
}
}
else
{
scoreLabel.text = NSString(format: "AVG. SCORE: %.0f", article.idealScore) as String
diffImage.image = UIImage(named: article.difficulty.lowercased() + "ScoreTag")
}
//Image
let resource = ImageResource(downloadURL: article.getImageURL(), cacheKey: article.objectId)
imageView.kf.setImage(with: resource)
UIHelper.addShadowToHomeCell((cell?.viewWithTag(1)!)!)
return cell!
}
var selectedIndex = -1
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if indexPath.row == self.objects!.count
{
self.loadNextPage()
}
else
{
selectedIndex = indexPath.row
self.performSegue(withIdentifier: "detailSegue", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "detailSegue"
{
let dest = segue.destination as? ArticlePreviewViewController
if UserDefaults.standard.bool(forKey: "showDemo")
{
dest?.article = DemoArticle().article
}
else
{
dest?.article = Article(parseData: self.objects![selectedIndex])
}
}
NotificationCenter.default.post(name: Notification.Name(rawValue: "CUCU_START"), object: nil)
}
override func queryForTable() -> PFQuery<PFObject>
{
let q = PFQuery(className: "Article").whereKey("releaseDate", lessThanOrEqualTo: Date()).whereKey("validated", equalTo: true)
if firstTime
{
q.fromLocalDatastore()
}
else
{
let mods = PFQuery(className: "Question").whereKey("article", matchesQuery: q)
mods.findObjectsInBackground
{
(objects, err) -> Void in
PFObject.pinAll(inBackground: objects)
}
}
print(firstTime)
totalArticles = q.countObjects(nil)
q.order(byDescending: "createdAt")
return q
}
}
Here are a few images of what the problem looks like:
Edit 1:
As suggested in the comments, I even tried to debug view hierarchy but I don't know which element to delete to make the grey bar go away.
It looks like it might be your navigation bar. Have you tried using this:
self.navigationController?.setNavigationBarHidden(true, animated: true)
instead of the statement you have of:
self.navigationController?.isNavigationBarHidden = true
Update #2:
Actually, I just realized you have that in the viewDidLoad. Move that to viewWillAppear and I bet it will work.
Update #3:
Just to reflect the true fix here, the offending code was in the viewDidAppear:
navigationController?.isNavigationBarHidden = false

UITableView is reloaded when presentviewcontroller or pushviewcontroller

I have a uitableview with some complex cells,
If I do presentviewcontroller or pushviewcontroller when I press the button in the cell,
it will reload data automatically,
how can i stop it?
this is the base Controller
import UIKit
class Controller: UIViewController{
override func viewDidLoad() {
super.viewDidLoad();
initView();
}
func initView(){
self.view.backgroundColor = UIColor.whiteColor();
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent;
}
}
and this is the second level
import Foundation
class LoadableController: Controller {
var requestNum : Int = 0;
func loadData(complete: (()->Void)? = nil){
self.requestComplete();
}
func requestComplete(){
if(self.requestNum == 0){
requestFinished();
return;
}
self.requestNum = self.requestNum > 0 ? self.requestNum - 1 : 0;
if(self.requestNum == 0){
requestFinished();
}
}
func requestFinished(){}
}
this is the third level
class TableListViewController: LoadableController, UITableViewDelegate, UITableViewDataSource {
var tableView : UITableView?;
var curPage : Int = 0;
var totalPage : Int = 0;
override func viewDidLoad() {
super.viewDidLoad()
}
override func initView() {
super.initView();
tableView = UITableView();
tableView!.delegate = self;
tableView!.dataSource = self;
tableView!.delaysContentTouches = false;
tableView!.canCancelContentTouches = true;
self.view.addSubview(tableView!);
tableView!.snp_makeConstraints { [unowned self](make) in
make.edges.equalTo(self.view);
}
let header = EGRefresh(refreshingTarget: self, refreshingAction: #selector(self.loadData));
header.setImages(Global.LOADING_IMAGES, duration: 0.16, forState: .Idle);
header.setImages(Global.LOADING_IMAGES, duration: 0.16, forState: .Pulling);
header.setImages(Global.LOADING_IMAGES, duration: 0.16, forState: .WillRefresh);
header.setImages(Global.LOADING_IMAGES, duration: 0.16, forState: .Refreshing);
header.lastUpdatedTimeLabel.hidden = true;
tableView!.mj_header = header;
let footer = MJRefreshBackNormalFooter.init(refreshingTarget: self, refreshingAction: #selector(self.loadNext));
tableView!.mj_footer = footer;
}
func loadNext(){
tableView!.mj_footer.endRefreshing();
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ListCell", forIndexPath: indexPath);
return cell;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1;
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
override func requestFinished() {
tableView!.mj_header.endRefreshing();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
deinit{
DDLogInfo("TableListViewController deinit");
}
}
and here is the place where the tableview reload automatically.
import UIKit
class CircleViewController: TableListViewController {
override func viewDidLoad() {
super.viewDidLoad();
}
override func initView(){
super.initView();
self.title = "Circle";
let items = ["One", "Two"];
let segment = UISegmentedControl(items: items);
segment.selectedSegmentIndex = 0;
segment.setWidth(80, forSegmentAtIndex: 0);
segment.setWidth(80, forSegmentAtIndex: 1);
self.navigationItem.titleView = segment;
tableView!.estimatedRowHeight = 50;
tableView!.rowHeight = UITableViewAutomaticDimension;
// tableView!.contentInset = UIEdgeInsetsMake(Dimens.TopBarHeight, 0, 0, 0);
tableView!.registerClass(TextOnlyCell.self, forCellReuseIdentifier: "CircleTextOnlyCell");
tableView!.registerClass(ImageAndPageCell.self, forCellReuseIdentifier: "CircleImageAndPageCell");
Event.addEventListener(self, selector: #selector(self.goComment(_:)), name: EventType.CIRCLE_COMMENT, object: nil);
}
func goComment(ns: NSNotification) {
let commentView = PostCommentViewController();
self.navigationController?.pushViewController(commentView, animated: true);
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension;
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10;
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true);
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CircleImageAndPageCell", forIndexPath: indexPath) as? ImageAndPageCell;
if cell == nil {
cell = ImageAndPageCell.init(style: .Default, reuseIdentifier: "CircleImageAndPageCell");
}
let images = [UIImage(named: "b.jpg")!, UIImage(named: "a.jpg")!, UIImage(named: "c.jpg")!, UIImage(named: "a.jpg")!];
cell?.initView(nil, nickname: "im a nickname", time: "10 min", content: NSMutableAttributedString(string: "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf"), likeNum: 10, commentNum: 12, shareNum: 13, totalLine: 20, images: images, cellIndex: indexPath.row, hasPage: true, pageIcon: UIImage(named: "c.jpg")!, titleStr: "ahahaha");
return cell!;
}
override func loadData(complete: (() -> Void)?) {
self.requestComplete();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
deinit {
Event.removeAllEventListener(self);
DDLogInfo("CircleViewController deinit");
}
}
If goComment be called,
the viewcontroller will be pushed,
then the tableview will jump to top,
I add a break point to the cellForRowAtIndexPath,
it's called.
then I tried the presentviewcontroller,
this problem also happened,
even though I did presentviewcontroller in the outside viewcontroller.
it seems not reload data,
it's just scrolls to the top.
it's looking like table view not taking the uitoolbar size(44 points) in account
Save the tableview offset in prepareForSegue: (save it in a CGPoint property)
self.tableViewScrollOffset = self.tableView.contentOffset;
Then, in viewWillAppear:, check if it has been modified. If so, restore it.
if(self.tableView.contentOffset.y != self.tableViewScrollOffset.y) {
[self.tableView setContentOffset:self.tableViewScrollOffset];
self.tableViewScrollOffset = CGPointZero;
}

Changing UITableViewController to UITableView crashes app

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)

Resources