tableView.reloadData() is not working - uitableview

I am making an app to keep track of my homework and for some reason when I call reloadData it doesn't work, some people have already asked about this but I have tried there fixes and they don't work for me, here is the file where I add new homework:
import UIKit
class AddHomework : UIViewController {
#IBOutlet weak var HomeworkNameLbl: UILabel!
#IBOutlet weak var HomeworkNameTxt: UITextField!
#IBOutlet weak var DueDateLbl: UILabel!
#IBOutlet weak var DueDateTxt: UITextField!
#IBOutlet weak var DueTimeLbl: UILabel!
#IBOutlet weak var DueTimeTxt: UITextField!
#IBOutlet weak var AddHomeworkBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboardWhenTappedAround()
}
#IBAction func AddHomework(sender: UIButton) {
let HomeworkName = HomeworkNameTxt.text
let DueDate = DueDateTxt.text
let DueTime = DueTimeTxt.text
homeworkTableView().AddObject([DueDate!, DueTime!], HomeworkName: HomeworkName!)
}
func hideKeyboardWhenTappedAround() {5
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
Here is the file where I have my tableview:
import UIKit
class homeworkTableView: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.reloadData()
}
var homework : [String: [String]] = [
"Spanish Test": ["Aug 12", "12:00 AM", "Spanish"],
"Math Sheet": ["Aug 13", "10:30 PM","Math"],
"Code Thing": ["Aug 11","12:00 AM","Coding"]
]
var titles = [
"Spanish Test", "Math Sheet", "Code Thing"
]
func AddObject(newArray: [String], HomeworkName: String){
titles.append(HomeworkName)
homework.updateValue(newArray, forKey: HomeworkName)
print(homework)
print(titles)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let Cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomCell
Cell.Title.text = titles[indexPath.row]
let currentTitle = titles[indexPath.row]
let current = homework[currentTitle]!
Cell.DueDate.text = "Due Date: \(current[0])"
Cell.Class.text = "Due Time: \(current[1])"
self.tableView.rowHeight = 100.00
return Cell
}
}
And the whole project is posted on git here:
https://github.com/IndyBob2019/HomeworkHelper
Any help is much appreciated, thanks!

You need to do what you do in viewDidLoad in viewWillAppear. viewDidLoad is only called at startup, so despite changing the data, your view never changes its own local data, so when you call reloadData it still uses the old data.
Taken from tableView reloadData does not work.

Here are some pointers based from your code,
First:
You're just calling -reloadData() once in viewDidLoad()..
Second:
If you want to see the changes made, it has to be in this order Update your -dataSource > -reloadData()
ex. from your code...
func AddObject(newArray: [String], HomeworkName: String){
titles.append(HomeworkName)
homework.updateValue(newArray, forKey: HomeworkName)
// check is changes was made
print(homework)
print(titles)
// reload after updating datasource
self.tableView.reloadData()
}
Edit:
After checking your project, i must say that your approach different from what i have in mind..
Calling calling -reloadData() once in viewDidLoad() is correct, but the problem is your
table dataSource which was updated inside File.swift by:
homeworkTableView().AddObject([DueDate!, DueTime!], HomeworkName: HomeworkName!)
You initialized homeworkTableView class and added or appended the data within that class
but upon presenting the homeworkTableView viewController from navigationController, you are
presenting new homeworkTableView class (not the homeworkTableView that was update)..
Calling self.tableView.reloadData() says:
fatal error: unexpectedly found nil while unwrapping an Optional value
because self.tableView was nil during the time of the update,
Here the solution I made:
First:
Globalizing your dataSource for Cross class updating, like so:
import Foundation
import UIKit
// I just moved your variables outside the class
//
var homework : [String: [String]] = [
"Spanish Test": ["Aug 12", "12:00 AM", "Spanish"],
"Math Sheet": ["Aug 13", "10:30 PM","Math"],
"Code Thing": ["Aug 11","12:00 AM","Coding"]
]
var titles = [
"Spanish Test", "Math Sheet", "Code Thing"
]
class homeworkTableView: UIViewController, UITableViewDelegate, UITableViewDataSource{
...
// codes
...
}
Second:
Removing self.tableView.reloadData() inside func AddObject(.. which i suggested before (without knowledge of your approach/implementation),
like:
func AddObject(newArray: [String], HomeworkName: String){
titles.append(HomeworkName)
homework.updateValue(newArray, forKey: HomeworkName)
// check is changes was made
print(homework)
print(titles)
}

Related

Updating label in UITableViewCell with UIStepper in Swift

I'm a Swift beginner and I'm trying to make a simple app for ordering food. The user could add a new order by setting food name, price and serving. After adding an order, that order will be shown on the tableView as a FoodTableViewCell, and the user could change the serving with an UIStepper called stepper in each cell. Each order is a FoodItem stored in an array called foodList, and you can see all orders listed in a tableView in ShoppingListVC.
My problem is: When I press "+" or "-" button on stepper, my servingLabel doesn't change to corresponding value. I tried to use NotificationCenter to pass serving value to stepper, and store new value back to food.serving after stepperValueChanged with delegate pattern. However, there still seems to be some bugs. I've been kind of confused after browsing lots of solutions on the Internet. Any help is appreciated.
Update
I removed NotificationCenter and addTarget related methods as #Tarun Tyagi 's suggestion. Now my UIStepper value turns back to 1 whereas the servingLabels are showing different numbers of serving. Since NotificationCenter doesn't help, how can I connect the label and stepper value together? Is it recommended to implement another delegate?
Here are my codes(Updated on July 8):
FoodItem
class FoodItem: Equatable {
static func == (lhs: FoodItem, rhs: FoodItem) -> Bool {
return lhs === rhs
}
var name: String
var price: Int
var serving: Int
var foodID: String
init(name: String, price: Int, serving: Int) {
self.name = name
self.price = price
self.serving = serving
self.foodID = UUID().uuidString
}
}
ViewController
import UIKit
class ShoppingListVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
var foodList = [FoodItem]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
...
for i in 1...5 {
let testItem = FoodItem(name: "Food\(i)", price: Int.random(in: 60...100), serving: Int.random(in: 1...10))
self.foodList.append(testItem)
}
}
// MARK: - Table view data source
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "foodCell", for: indexPath) as! FoodTableViewCell
let food = foodList[indexPath.row]
cell.nameLabel.text = food.name
cell.priceLabel.text = "$\(String(food.price)) / serving"
cell.servingLabel.text = "\(String(food.serving)) serving"
cell.stepper.tag = indexPath.row
cell.delegate = self
return cell
}
}
// MARK: - FoodTableViewCellDelegate Method.
extension ShoppingListVC: FoodTableViewCellDelegate {
func stepper(_ stepper: UIStepper, at index: Int, didChangeValueTo newValue: Double) {
let indexPath = IndexPath(item: index, section: 0)
guard let cell = tableView.cellForRow(at: indexPath) as? FoodTableViewCell else { return }
let foodToBeUpdated = foodList[indexPath.row]
print("foodToBeUpdated.serving: \(foodToBeUpdated.serving)")
foodToBeUpdated.serving = Int(newValue)
print("Value changed in VC: \(newValue)")
cell.servingLabel.text = "\(String(format: "%.0f", newValue)) serving"
}
}
TableViewCell
import UIKit
protocol FoodTableViewCellDelegate: AnyObject {
func stepper(_ stepper: UIStepper, at index: Int, didChangeValueTo newValue: Double)
}
class FoodTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var priceLabel: UILabel!
#IBOutlet weak var servingLabel: UILabel!
#IBOutlet weak var stepper: UIStepper!
weak var delegate: FoodTableViewCellDelegate?
#IBAction func stepperValueChanged(_ sender: UIStepper) {
sender.minimumValue = 1
servingLabel.text = "\(String(format: "%.0f", sender.value)) serving"
// Pass the new value to ShoppingListVC and notify which cell to update using tag.
print("sender.value: \(sender.value)")
delegate?.stepper(stepper, at: stepper.tag, didChangeValueTo: sender.value)
}
override func awakeFromNib() {
super.awakeFromNib()
print(stepper.value)
}
}
Initially FoodTableViewCell is the ONLY target for UIStepper value changed (looking at #IBAction inside FoodTableViewCell).
When you dequeue a cell to display on screen, you call -
cell.stepper.addTarget(self, action: #selector(stepperValueChanged(_:)), for: .valueChanged)
which causes your ShoppingListVC instance to be added as an additional target every time a cellForRow call is executed.
Things to fix :
Remove all of your NotificationCenter related code from both classes.
Remove cell.stepper.addTarget() line as well.
This would give you a better idea of why it is happening this way. Update your question with these changes in case you still don't have what you want.
UPDATE
// Inside cellForRow
cell.stepper.value = food.serving
Cell Config:
protocol FoodTableViewCellDelegate: AnyObject {
func stepper(sender: FoodTableViewCell)
}
#IBAction func stepperButtonTapped(sender: UIStepper) {
delegate?.stepperButton(sender: self)
stepperLabel.text = "\(Int(countStepper.value))"
}
Controller Config:
cellForRow:
cell.countStepper.value = Double(foodList[indexPath.row].serving);
cell.stepperLabel.text = "\(Int(cell.countStepper.value))"
Delegate Method:
func stepperButton(sender: FoodTableViewCell) {
if let indexPath = tableView.indexPath(for: sender){
print(indexPath)
foodList[sender.tag].serving = Int(sender.countStepper.value)
}
}
Please check value stepper pod it will help you: Value stepper
Integrate value stepper pod and use below code for basic implementation.
import ValueStepper
let valueStepper: ValueStepper = {
let stepper = ValueStepper()
stepper.tintColor = .whiteColor()
stepper.minimumValue = 0
stepper.maximumValue = 1000
stepper.stepValue = 100
return stepper
}()
override func viewDidLoad() {
super.viewDidLoad()
valueStepper.addTarget(self, action: "valueChanged:", forControlEvents: .ValueChanged)
}
#IBAction func valueChanged1(sender: ValueStepper) {
// Use sender.value to do whatever you want
}
Its simplify custom stepper implantation.Take outlet of value stepper view in table tableview and use it.

SIGABRT error in dequeueReusableCell(withIdentifier:), when using custom UITableViewCell

I'm building an app with multiple scenes and a table view with custom cells in each. I got the home screen table view to work fine and then I segue to the new scene from the custom cells. When it segues, my second view controller crashes.
Here is my code for the view controller
import UIKit
class QuestionViewController: UIViewController {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var submitButton: UIButton!
#IBOutlet weak var qTableView: UITableView!
var answers : [QuestionOption] = []
override func viewDidLoad() {
super.viewDidLoad()
answers = [QuestionOption(text: "test"), QuestionOption(text: "test"), QuestionOption(text: "test"), QuestionOption(text: "test")]
qTableView.delegate = self
qTableView.dataSource = self
submitButton.setTitle("Submit", for: .normal)
questionLabel.text = "test question"
}
}
extension QuestionViewController: UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return answers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let a = answers[indexPath.row]
let cell = qTableView.dequeueReusableCell(withIdentifier: "QuestionOptionCell") as! QuestionOptionCell
cell.setOption(option: a)
return cell
}
}
Here's my code for the cell
import UIKit
class QuestionOptionCell: UITableViewCell {
#IBOutlet weak var cellTitle: UILabel!
func setOption(option: QuestionOption){
cellTitle.text = option.text
}
}
Here's my code for the QuestionOption class
import Foundation
import UIKit
class QuestionOption{
var text: String
init(text: String){
self.text = text
}
}
Crash log
2019-02-20 14:33:28.394695-0800 iQuiz[8935:822409] *** NSForwarding: warning: object 0x7fd608407c40 of class 'iQuiz.QuestionOption' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[iQuiz.QuestionOption initWithCoder:]
2019-02-20 14:33:28.395281-0800 iQuiz[8935:822409] Unrecognized selector -[iQuiz.QuestionOption initWithCoder:]
Here's my storyboard if that helps at all
I've made sure my identifier matches and I don't have any extraneous or unconnected outlets, those are the only solution to this problem I can find online.
The crash log says that QuestionOption must be a subclass of NSObject and adopt NSCoding which is overkill in this case. Actually a struct would be sufficient.
You can avoid it by deleting the method in QuestionOptionCell
func setOption(option: QuestionOption){
cellTitle.text = option.text
}
and set the value in cellForRowAt directly by replacing
cell.setOption(option: a)
with
cell.cellTitle.text = a.text
Things to check:
Verify that "QuestionOptionCell" is indeed the reuse identifier for the cell.
Verify that the selected type for the cell is QuestionOptionCell.
In cellForRowAt, use tableView.dequeueReusableCell instead of qTableView.dequeueReusableCell.
Otherwise, share the crash log with us.

DTTableViewmanager: Cell mapping is missing for model

I am using a DTTableViewManager for my UIViewController with a UITableView contained within.
My View Controller only contains:
class ViewController: UIViewController, DTTableViewManageable {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let users = ["Test User 1", "Test User 2"]
manager.startManaging(withDelegate: self)
manager.register(PersonsCell.self)
manager.memoryStorage.addItem(users, toSection: 0)
}
}
This is conform their Github example
My UITableViewCell looks as follows:
class PersonsCell: UITableViewCell, ModelTransfer {
func update(with model: String) {
self.nameLabel = model
}
#IBOutlet weak var nameLabel: UILabel!
}
This is also conform their example on Github.
But still I'm getting this error:
fatal error: Cell mapping is missing for model: ["Test User 1", "Test User 2"]: file ~~ ~/Pods/DTTableViewManager/Source/DTTableViewManager.swift, line 737
I have tried to find examples for people with similar problems but I could not find them.

Custom TableView functions are not getting called?

On my main.Storyboard I have a TableViewController which is set to a custom class with TableViewController.swift.
The swift file has all the tableview functions defined and the #IBOutlet for the UITableView connected. The classes defined are UINavigationController,UITableViewDelegate. This viewController is called from a secondViewController via the prepareForSegue function.
I also created CustomCell.swift with class UITableViewCell and all #IBOutlet for the labels in my UITableViewCell which has been set to the customCell class.
I can't paste all my code but if you need to look at any specific code let me know and I will be happy to post that.
The Build succeeds and the app runs but the tableviewcells don't show up and none of the tableview functions are called. I see 2 flash animated screens - indicating that the tableviewcell might have 2 views - but can't figure out where I should be checking?
//Below is the segue function triggering the TableViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//print("In prepareswgue: ",segue, " ",sender)
if(segue.identifier == "resultSegue")
{
let nav = segue.destinationViewController as! UINavigationController
let svc = nav.topViewController as! TableViewController
svc.serialNo = self.TSSerialNoField.text
}
}
//Below is the custom TableVIewController class code
class TableViewController: UINavigationController,UITableViewDataSource, UITableViewDelegate
{
var serialNo:String!
var ashHardwareData: NSMutableArray!
#IBOutlet var ResultTableView: UITableView!
//#IBOutlet weak var LogCaseButton: UIButton!
#IBOutlet weak var TypeResultLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.ResultTableView?.allowsSelectionDuringEditing = true
self.ResultTableView?.delegate = self
ResultTableView?.dataSource = self
}
override func viewDidAppear(animated: Bool) {
self.getHardwareData(serialNo.uppercaseString)
}
/*override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
}*/
func getHardwareData(serialno:String)
{
ashHardwareData = NSMutableArray()
ashHardwareData = ModelManager.getInstance().getHardwareData(serialno)
ResultTableView?.reloadData()
}
//TableView Delegate Methods
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
print("In height func")
return 50
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(ashHardwareData.count)
return ashHardwareData.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:ResultsCell = tableView.dequeueReusableCellWithIdentifier("results", forIndexPath: indexPath) as! ResultsCell
let hardware:HardwareInfo = ashHardwareData.objectAtIndex(indexPath.row) as! HardwareInfo
let contract:ContractInfo = ashHardwareData.objectAtIndex(indexPath.row) as! ContractInfo
cell.SNOLabel.text = "Serial N0: \(hardware.SerialNo)"
cell.ContractIDLabel.text = "Contract ID: \(contract.ContractID)"
cell.OrgLabel.text = "Organisation: \(hardware.Organisation)"
cell.ModelLabel.text = "Model: \(hardware.Model)"
if(contract.DaystoExpiry > 0) {
cell.TypeLabel.text = "Contract Type: Active"
self.TypeResultLabel.hidden = false
self.TypeResultLabel.text = "To log a technical case for the Hardware please click on Log Technical Case button."
cell.LogCaseButton.hidden = false
cell.LogCaseButton.tag = indexPath.row
}
else {
cell.TypeLabel.text = "Contract Type: Expired"
cell.LogCaseButton.hidden = true
self.TypeResultLabel.hidden = false
self.TypeResultLabel.text = "Support Contract for the hardware expired. Please contact Sales team to renew the contract."
}
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//here is the code for the ResultsCell custom UITableViewCell
import Foundation
import UIKit
class ResultsCell: UITableViewCell {
#IBOutlet weak var SNOLabel: UILabel!
#IBOutlet weak var LogCaseButton: UIButton!
#IBOutlet weak var ContractIDLabel: UILabel!
#IBOutlet weak var OrgLabel: UILabel!
#IBOutlet weak var TypeLabel: UILabel!
#IBOutlet weak var ModelLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
have you configured ResultsCell in story board or in view didload. check it once. if not try to add the following code into your view didload
[self.tableView registerNib:[UINib nibWithNibName:#"ResultsCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:#"ResultsCell"];
I have spent fair bit of my time trying to get this issue resolved and decided to go without the tableviewcell, instead I have created custom viewControllers and defined labels to display the results from the DB search (which is working OK) and used the perform segue function to pass values between viewcontrollers. So now my app works the way I want - maybe not ideal from a programming side but due to time constraints I had to get this working.
I have my app as a Tabbed Application with First and Second ViewControllers. The FirstViewController adds data to the SQLLite DB and the Second searches for the data and displays the results. for displaying the results I created a Custom viewController with labels for the data that I wanted displayed and passed all data from the DB results in SecondViewController functions to it and updated the labels with the data. As long as I get the results I wanted I am happy. I will re-visit this for improvement if I have to. Thanks to all who responded with solutions and suggestions. It has been a good learning experience :)

Accessing custom table cell labels

I have created two custom labels in a table cell in order to be able to dynamically resize the cell(s) to it's content. I first tried using the "Subtitle" style and this worked out great except that the cell(s) didn't resize the way i wanted to and it looked really messy.
My question is: how do I access these labels in order to append my value's from my API to them?
View controller code:
import UIKit
class nyheterViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, APIControllerProtocol {
#IBOutlet weak var nyheterTableView: UITableView!
#IBOutlet weak var titleLabel: UILabel!
var searchResultsData: NSArray = []
var api: APIController = APIController()
func JSONAPIResults(results: NSArray) {
dispatch_async(dispatch_get_main_queue(), {
self.searchResultsData = results
print(self.searchResultsData)
self.nyheterTableView.reloadData()
})
}
override func viewDidLoad() {
super.viewDidLoad()
var APIBaseUrl: String = "http://*.se/*/*.php"
var urlString:String = "\(APIBaseUrl)"
//Call the API by using the delegate and passing the API url
self.api.delegate = self
api.GetAPIResultsAsync(urlString, elementName:"news")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print(self.searchResultsData.count)
return self.searchResultsData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier: String = "nyheterResultsCell"
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as UITableViewCell
//nyheterTableViewCell.cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier);
//Create a variable that will contain the result data array item for each row
var cellData: NSDictionary = self.searchResultsData[indexPath.row] as NSDictionary
//Assign and display the Title field
var releaseDate: String = cellData["date"] as String
var titleVar: String = cellData["title"] as String
var titleMix: String = "\(titleVar)" + " - " + "\(releaseDate)"
cell.textLabel?.text = titleMix //textLabel worked out fine using "Subtitle" style.
// Get the release date string for display in the subtitle
cell.detailTextLabel?.text = cellData["content"] as String? //Same
return cell
}
}
I understand that I can't access these labels without somehow connecting them to the ViewController. Creating outlets to the ViewController generates an error about that I can't use connections from the prototype cell to the ViewController.
So, i created a new class, called nyheterTableViewCell which i connect to the table cell and connected outlets to my labels.
nyhterTableViewCell code:
import UIKit
class nyheterTableViewCell: UITableViewCell {
#IBOutlet weak var nyhetLabel: UILabel!
#IBOutlet weak var titleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I'm an beginner at Swift-programming and Xcode.
Cheers!
You don't need the labels connected to the view controller. Your example of the custom table view cell looks correct.
To access the label properties, you're going to want to change the line
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as UITableViewCell
to
let cell: nyheterTableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as nyheterTableViewCell

Resources