My TableView's not changed after reload data - swift

I wrote a class that let me create multiple tableView simply. When I call this class for the first time, everything work well. But when I change some data, and reload the table, nothing changed.
Sample code:
class TestViewController: UIViewController {
var arrData = ["a","b","c"]
var myTableView: MyTableView?
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
myTableView = MyTableView(table: tableView, data: arrData)
}
#IBAction func buttonTapped(_ sender: UIButton) {
arrData = ["d","e","f"]
myTableView!.tableView.reloadData() //=> Not change anything
}
}
class MyTableView: NSObject, UITableViewDataSource {
var tableView: UITableView
var data: Array<String>
init(table: UITableView, data: Array<String>) {
self.data = data
self.tableView = table
super.init()
self.tableView.dataSource = self
self.tableView.register(MyTableViewCell.self, forCellReuseIdentifier: "myCell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.data.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
cell.textLabel!.text = self.data[indexPath.row]
return cell
}
}
class MyTableViewCell : UITableViewCell {
//something here
}
When the view was loaded, the table has 3 rows: a,b,c. When I tap the button, nothing changed (expected: d,e,f)
Please help me!

Swift arrays are copied by value so the line self.data = data will take a copy of your array. Later changing the array contents of the source will not be reflected in the copy in your MyTableView.
You'll need to pass the array over again and take a second copy to update the table, e.g. write a method in MyTableView similar to the following:-
func setNewValues(data: Array<String>)
{
self.data = data
self.tableView.reloadData()
}
and call that from your buttonTapped function, i.e.:
#IBAction func buttonTapped(_ sender: UIButton) {
arrData = ["d","e","f"]
myTableView!.setNewValues(data: arrData)
}
Be careful with the force-unwrapped myTableView though - I'd replace that '!' with '?'.

Related

How to get value from table view cell

I need to take price from table view cell
I need to get it another VC
I need to transfer price each time when user selected row
The problem is that I don't know how correctly get the value from tableView
MenueViewController:
import UIKit
class MenueViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var dishes: [Dish] = []
var totalSum = 0
override func viewDidLoad() {
super.viewDidLoad()
dishes = createArray()
tableView.delegate = self
tableView.dataSource = self
tableView.backgroundColor = .white
navigationItem.title = "Меню"
}
func createArray() -> [Dish] {
var tempDishes: [Dish] = []
let dish1 = Dish(image: UIImage.init(named: "plovKebab")!, title: "Плов Кебаб", price: 169, type: "Основные Блюда")
let dish2 = Dish(image: UIImage.init(named: "plovKebabShafran")!, title: "Плов Кебаб Шафран", price: 169, type: "Основные Блюда")
tempDishes.append(dish1)
tempDishes.append(dish2)
return tempDishes
}
}
extension MenueViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dishes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let dish = dishes[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "MenueCell") as! MenueCell
cell.setDish(dish: dish)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let dish = dishes[indexPath.row]
totalSum += dish.price //the place where I tried to take price
print(totalSum)
}
}
VC where we need to take that price:
import UIKit
class OrderViewController: UIViewController {
#IBOutlet weak var totalPriceLabel: UILabel!
var totalPrice: MenueViewController?
var sum:Int?
override func viewDidLoad() {
super.viewDidLoad()
if let price = sum {
totalPriceLabel.text = String(totalPrice?.totalSum)
}
}
The value of sum is 0
How can I get the value?
Please note that the didSelectRowAtIndexpath will not get called before your performSegue. At this link, you can find a detailed explanation including a sample code about your problem and how it can be solved.
Also, there are multiple approaches listed below to solve your problem.
Use willSelectIndexpath and capture the indexpath to pass it in performSegue method
Use didSelectIndexpath and perform segue inside
Use only didSelectIndexpath and perform the navigation logic inside it with out segues

How to refresh tableview when user inserts an item into the database?

When the user inserts an item into the database and then clicks the back button to return to the tableview, the tableview is not displaying the new item. I have to stop the app, and run it again for the new item to be displayed. My database is working perfectly fine, so i don't think thats the problem.
I do reload the database again in the mainController inside ViewDidLoad(). I also tried doing tableView.reloadData() inside ViewDidLoad() as well and that doesn't do anything.
class MainViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView?
let mainDelegate = UIApplication.shared.delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
// read from the database
mainDelegate.readDataFromDatabase()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return mainDelegate.people.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BookTableViewCell") as! BookTableViewCell
let rowNum = indexPath.row
cell.lblTitle?.text = mainDelegate.people[rowNum].title
cell.accessoryType = .disclosureIndicator
return cell
}
class NewBookItemViewController: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate {
#IBOutlet weak var titletxt : UITextField!
#IBAction func buttonSave(sender: UIButton){
// step 18b - instantiate Data object and add textfield data
let person : Data = Data.init()
person.initWithData(theRow: 0, thetitle: title)
let mainDelegate = UIApplication.shared.delegate as! AppDelegate
// step 18c - do the insert into db
let returnCode : Bool = mainDelegate.insertIntoDatabase(person: person)
}
You need
override func viewWillAppear(_ animated:Bool) {
super.viewWillAppear(animated)
mainDelegate.readDataFromDatabase()
tableView.reloadData()
}
As viewDidLoad is called once when the vc is loaded , also it's not the responsiliblity of the appDelegate to do such things , conside having a dataSource class that handle read/write from the database

TableViewController issue: won't save user input from textView

Update:
so goal of this to save the text that user enters in a UITextView in cell, so that the text is saved for that particular cell number and does not duplicate, move or remove the text.
as suggested I am trying to handle the textViewdidChange func inside of the custom cell, by doing the following:
var onTextEntered: ((String?) -> ())!
func setup() {
notesTextView.delegate = self
}
func textViewDidChange(_ textView: UITextView) {
onTextEntered(notesTextView.text)
}
making a string that holds the text and then adding the text to the String every time textViewDidChange is called (trying to explain this to myself as we go along, so please correct me if my explanation needs it).
next in CellForRowAt
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "TableViewNotesCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! TableViewNotesCell
cell.setup()
cell.onTextEntered = {input in
if let text = input {
self.cellText[indexPath.row] = text // here is the error (index out of range)
}
if indexPath.row < self.cellText.count {
cell.notesTextView.text = self.cellText[indexPath.row] ?? "placeholder"
}
}
return cell
}
when I do the above code, as soon as textViewDidChange is called (when i type a single letter or digit in textView) the I get the error : "Fatal error: Index out of range" on the line which I use the array of cellText[indexPath.row] = text. please help or let me know if my understanding of the process is wrong would love to learn!
You can try to save for every single edit
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// inside cellForRowAt
cell.textView.delegate = self
cell.textView.tag = indexPath.row
return cell
}
#objc func textViewDidChange(_ tex: UITextView) {
cellEndEdit[tex.tag] = tex.text!
}
class VC:UIViewController,UITextViewDelegate {
give default values for the array
var cellEndEdit = [String](repeating: "", count: numberOfRows)
Assuming, that you tableView has a variable number of cells, and all of them have a UITextView, whose content should be recorded and indexed, I would recommend to create a custom UITableViewCell, that handles the textView itself.
class CustomTableViewCell: UITableViewCell, UITextViewDelegate {
#IBOutlet weak var textView: UITextView()
var onTextEntered: ((String?) -> ()) //that's a callback
func setup() {
textView.delegate = self
}
override func textViewDidChange(textView: UITextView) {
onTextEntered(textView.text)
}
}
Since you're working with a sorted list of user inputs, you should have your array at hands, in which you can store and from which you can retrieve data. So if some data already exists, grep through your array and fill the cells that deserve it. Also define the onTextEntered callback here to tell the cell what to do, if it gets called (in your case, store the text of the UITextView in your array).
//your carrier, if you store the already existing user inputs some where, map them in here
//depending on how you calculate the number of cells, this array has to have the same size so use the same logic for this
var yourStringCarrierArray: [String?] = []
override func tableView(_ tableview: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourTableViewCell", for: indexPath) as! CustomTableViewCell
cell.setup()
cell.onTextEntered = { input in
if let text = input {
self.yourStringCarrierArray[indexPath.row] = text
}
if indexPath.row < yourStringCarrierArray.count {
cell.textView.text = yourStringCarrierArray[indexPath.row] ?? "placeholder string, because there's no input here so far"
}
}
I hope this will help or at least give you a new perspective, it has been a while, i coded in Swift. Feel free to ask me, if something is unclear.
Use an object to save the string value, since String in swift is value type. Here is an example:
class TestViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var dataArray: [CellData] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.delegate = self
tableView.dataSource = self
for _ in 0..<20 {
dataArray.append(CellData())
}
}
}
extension TestViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableViewCell", for: indexPath) as! TestTableViewCell
cell.setData(data: dataArray[indexPath.row])
return cell
}
}
class TestTableViewCell: UITableViewCell {
#IBOutlet weak var textView: UITextView!
var data: CellData!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
textView.text = ""
textView.delegate = self
}
func setData(data: CellData) -> Void {
self.data = data
self.textView.text = data.stringValue
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension TestTableViewCell: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
self.data.stringValue = textView.text
}
}
class CellData {
var stringValue: String = ""
}

What am I doing wrong on trying to set data to Struct

I'm trying to create a Struct to be able to pass data between screens. However, when I set data to my Struct properties and then I try to use it in another screen I got the value as "nil". What am I doing wrong?
Objects Struct:
import UIKit
struct Objetos {
var nome: String
var foto: UIImage
}
That's how I'm trying to set the value:
var objeto = Objeto(nome: "", foto: UIImage())
#IBAction func botaoAdcItem(_ sender: UIButton) {
if (self.namePreview!.text != nil) && (self.imagePreview!.image != nil) {
objeto?.nome = self.namePreview.text!
objeto?.foto = self.imagePreview.image!
self.navigationController?.popViewController(animated: true)
}
else { return }
}
That's how I'm trying to read the data:
class ViewController: UIViewController, CLLocationManagerDelegate, UITableViewDataSource {
#IBOutlet weak var itensTableView: UITableView!
var arrayNomes = NSMutableArray()
var objeto: Objetos?
var objetos = [Objetos]()
//TableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
for nome in objetos {
arrayNomes.add(nome)
}
return arrayNomes.count //Nil value
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
cell.nameCell.text = objeto?.nome //Nil value
cell.imageViewCell.image = objeto?.foto //Nil value
return cell
}
override func viewDidAppear(_ animated: Bool) {
self.itensTableView.reloadData()
}
Storyboard:
In your second code snippet, you never initialize objetos to anything. Thus, when you try to set one of the properties on objetos, nothing happens, because objetos doesn't exist.
Solution: Initialize objetos before trying to use it.
You can try this if you want to set the data to the previous VC
// ! won't crash
let count = self.navigationController.viewControllers!.count
let tab = self.navigationController.viewControllers![count-2] as! UITabBarController
let vc = tab.viewControllers![0] as! ViewController
vc.objetos.append(Objetos(name:self.namePreview.text!,foto:self.imagePreview.image!))
self.navigationController?.popViewController(animated: true)
plus
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = objectos[indexPath.row]
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
cell.nameCell.text = item.nome //Nil value
cell.imageViewCell.image = item.foto //Nil value
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objetos.count
}
Try to replace
var objetos:Objetos?
with
var objeto = Objetos(nome: "", foto: UIImage())
you are starting your object as optional, and thus from the start is nil. When you try to assign properties in your struct you are failures because the object from the beginning is nonexistent
Regarding you other nil values, here is a revised view of your snippet
class ViewController: UIViewController, CLLocationManagerDelegate, UITableViewDataSource {
#IBOutlet weak var itensTableView: UITableView!
var objetos = [Objetos]()
//TableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objetos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
let objeto = objectos[indexPath.row]
cell.nameCell.text = objeto.nome
cell.imageViewCell.image = objeto.foto
return cell
}
override func viewWillAppear(_ animated: Bool) {
self.itensTableView.reloadData()
}
In your Additional Novo Itens Vc you are going to need to pass the created data back to your original View Controller, one way to do this is
#IBAction func botaoAdcItem(_ sender: UIButton) {
if (self.namePreview!.text != nil) && (self.imagePreview!.image != nil) {
// create the objecto
var objeto = Objetos(nome: namePreview.text,
foto: imagePreview.image)
// create a reference to your previous view controller
let vc = self.navigationController?.viewControllers[0] as! ViewController
// add objecto to objectos
vc.objetos.append(objeto)
self.navigationController?.popViewController(animated: true)
}
else { return }
}

tableview to tableview protocol delegates

I am trying to finish this app that is a form. once filled out a button later will be pushed for print. The first controller has a tableview that has page 1 page 2 page 3. page 1 opens and you fill in all info. when hit save it should take you back to first controller. then when you push print opens the tableview and loads all info.
I am struggling to use structs correctly. also struggling on the save button to the delegate to the print controller page.
my page controller code
class PagesController: UIViewController, UITableViewDelegate {
var pages = ["Page 1","Page 2","Page 3"]
#IBOutlet weak var pagesTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
pagesTableView.delegate = self
pagesTableView.dataSource = self
}
#IBAction func printBtnPressed(_ sender: Any) {
} }
extension PagesController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PagesCell")
cell?.textLabel?.text = pages[indexPath.row]
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
performSegue(withIdentifier: "ChildInfoSegue", sender: self)
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90.0
}
}
Heres my form controller
protocol ChildDelegate {
func saveInfoBlock(form: ChildInfo)
}
class ChildInfoTableViewController: UITableViewController {
var formDelegate: ChildDelegate!
var firstNameText: UITextField = UITextField()
var lastNameText: UITextField = UITextField()
var middleNameText: UITextField = UITextField()
//MARK: From ChildInfo.swift STRUCT
var childInfo = [ChildInfo]()
var basicChildInfo = ["Child's First Name","Child's Middle Name","Child's Last Name"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return basicChildInfo.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ChildInfoCell") as! ChildInfoTableViewCell
cell.formLabel.text = basicChildInfo[indexPath.row]
return cell
}
//MARK: Segue to printController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let path = self.tableView.indexPathForSelectedRow!
let destViewController = segue.destination as! PrintPageTableViewController
}
//MARK: Cancel Button Pressed
#IBAction func cancelBtnPressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func saveBtnPressed(_ sender: Any) {
let firstname = firstNameText.text ?? ""
let middlename = middleNameText.text ?? ""
let lastname = lastNameText.text ?? ""
let formData = ChildInfo(cFirstName: firstname, cMiddleName: middlename, cLastName: lastname)
self.navigationController?.popViewController(animated: true)
formDelegate.saveInfoBlock(form: formData)
saveInfoBLock()
}
func saveInfoBLock() {
print("Saving Info")
dismiss(animated: true, completion: nil)
}
}
Heres my struct that I don't properly use
struct ChildInfo {
var childFirstName: String?
var childMiddleName: String?
var childLastName: String?
var childsMadeUpName: String = ""
init(cFirstName: String, cMiddleName: String, cLastName: String) {
self.childFirstName = cFirstName
self.childMiddleName = cMiddleName
self.childLastName = cLastName
}
}
and lastly my tableviewcell
class ChildInfoTableViewCell: UITableViewCell {
#IBOutlet weak var formText: UITextField!
#IBOutlet weak var formLabel: 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
}
}
Once the delegate protocol gets passed. What would a put in the printtableviewcontroller to make the data show? I had someone help me on here, but It was all programmatically and I'm no that advanced yet.
There are several things you can do:
Use delegation. I am assuming this is what you want to achieve here.
Pass your data through Segues. ( Recommended )
To use delegation, you have to have a clear concept of how delegation works.
Here is a link to an explanation I gave someone on another site. It breaks it down, I think this may help you a bit.
https://teamtreehouse.com/community/totally-confused-with-the-example-used
For now, I think the solution to your problem is to use Segues.
When you tap on the button to take you to the ChildInfoTableViewController where you fill the information is where you want to create your "ChildInfo" object. Then when you press save, you trigger the segue and pass the "ChildInfo" object to the "PagesController".
Then from there, that same object should be pass through the segue into the print view.