TableView is not appending new data - ios

I am trying to create a simple app which lets appending data into an array in first ViewController and then send it to the SecondViewController through the segue. However instead of appending new data it is updating first index of an array.
import UIKit
class ViewController: UIViewController {
var data = [String]()
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func sendButton(_ sender: Any) {
if textField.text != nil {
let activity = textField.text
data.append(activity!) //Data should be appended here.
performSegue(withIdentifier: "sendSegue", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "sendSegue" {
let destVC: AppTableViewController = segue.destination as! AppTableViewController
destVC.newData = data
}
}
}
Here is my SecondTableViewController and it just update first index so I am just keep changing my first index instead of adding new values into it.
import UIKit
class AppTableViewController: UITableViewController {
var newData = [String]()
#IBOutlet var myTableView: UITableView!
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 {
return newData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = newData[indexPath.row]
return cell
}
}

Based on this link,
Please change the function name sendButton to btnSendButton.
So that it will run before prepare.
As of now sendButton is running after prepare. So the data is not appending.

Related

add a row to the tableView from another viewController

what I did so far:
I create a tableView controller with empty rows, and a popup window on another ViewController, the purpose of the popup is to add two data(name - link) to the tableView on one ROW (passing Textfields).
what I want :
when I add a new raw (name - link) click save , I want the data to be stored , when I add other data, create another row .
my problem: when I tray to add more raws, it's always wright over the old one, so no matter how much data I input, the result always be 1 row!
popup VC
#IBOutlet weak var nameP: UITextField!
#IBOutlet weak var linkP: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let SEC: TEstVC = segue.destination as! TEstVC
SEC.add(name: nameP.text!, link: linkP.text!)
}
HomeScreen VC
class TEstVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var names = [String]()
var links = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 100.0
tableView.dataSource = self
tableView.delegate = self
}
func add(name: String, link: String) {
names.append(name)
links.append(link)
}
override func viewWillAppear(_ animated: Bool) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count //or links.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.cell4Name.text = names[indexPath.row]
cell.cell4Link.text = links[indexPath.row]
return cell
}
Move your array declaration to global and call reloadData in your tableView like this:
//Popup
#IBOutlet weak var nameP: UITextField!
#IBOutlet weak var linkP: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let SEC: TEstVC = segue.destination as! TEstVC
SEC.add(name: nameP.text!, link: linkP.text!)
self.tableView.reloadData()
}
Your VC
var names = [String]()
var links = [String]()
class TEstVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 100.0
tableView.dataSource = self
tableView.delegate = self
}
func add(name: String, link: String) {
names.append(name)
links.append(link)
}
override func viewWillAppear(_ animated: Bool) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count //or links.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.cell4Name.text = names[indexPath.row]
cell.cell4Link.text = links[indexPath.row]
return cell
}
Simple, after you add a new item to the datasource (names) you need to reload the tableview.
self.tableView.reloadData()
Well, it looks like you're going to a completely new instance of TestVC every single time. So, it's not overwriting the old line. It's putting a new line in a new instance of TestVC. I infer this because you're calling add(name:link:) in prepareForSegue, which gets called by the storyboard when a new viewController is being put on screen with a segue.
Also, your add(name:link:) method should call tableView.reloadData() if you want it to update the screen.

Pass the myCell.textLabel?.text value via a segue in a dynamic prototype

I'm trying to segue from one UITableView to another UITableView. I want to segue and pass the myCell.textLabel?.text value of the selected cell to the second UITableView.
The code for my first UITableView (MenuTypeTableViewController and the code for my second UITableView (TypeItemsTableViewController) is also below.
I'm fully aware this involves the prepareForSegue function which currently I've not created, purely because I'm unsure where I override it and how to pass in the textLabel value to it.
Hope my question makes sense, I will update with suggestions and edits.
class MenuTypeTableViewController: UITableViewController, MenuTypeServerProtocol {
//Properties
var cellItems: NSArray = NSArray()
var menuType: MenuTypeModel = MenuTypeModel()
override func viewDidLoad() {
super.viewDidLoad()
let menuTypeServer = MenuTypeServer()
menuTypeServer.delegate = self
menuTypeServer.downloadItems()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier: String = "cellType"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
let item: MenuTypeModel = cellItems[indexPath.row] as! MenuTypeModel
myCell.textLabel?.text = item.type
return myCell
}
func itemsDownloaded(items: NSArray) {
cellItems = items
tableView.reloadData()
}
}
class TypeItemsTableViewController: UITableViewController, TypeItemsServerProtocol {
//Properties
var cellItems: NSArray = NSArray()
var typeItemList: TypeItemsModel = TypeItemsModel()
override func viewDidLoad() {
super.viewDidLoad()
let typeItemsServer = TypeItemsServer()
typeItemsServer.delegate = self
typeItemsServer.downloadItems()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier: String = "cellTypeItem"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
let item: TypeItemsModel = cellItems[indexPath.row] as! TypeItemsModel
myCell.textLabel?.text = item.name
return myCell
}
func itemsDownloaded(items: NSArray) {
cellItems = items
tableView.reloadData()
}
}
Hi try the following set of code, I have added few additional changes in your code make use of it, I hope it will solve your issue.
I have added only the extra codes which you needed
class TypeItemsTableViewController: UITableViewController, TypeItemsServerProtocol {
// Add this variable in this class and use it whereever you needed it in this class
var selectedItem: String?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Get the selected cell
let selectedCell = tableView.cellForRow(at: indexPath)
// Now maintain the text which you want in this class variable
selectedItem = selectedCell?.textLabel?.text
// Now perform the segue operation
performSegue(withIdentifier: "TypeItemsTableViewController", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TypeItemsTableViewController" {
let destinationVC = segue.destination as? TypeItemsTableViewController
destinationVC?.selectedItem = self.selectedItem // Pass the selected item here which we have saved on didSelectRotAt indexPath delegate
}
}
In Second class:
class TypeItemsTableViewController: UITableViewController, TypeItemsServerProtocol {
// Add this variable in this class and use it whereever you needed it in this class
var selectedItem: String?
What you can do is to make a variable in your second UITableView
var String: labelSelected?
then in you prepare for segue method just set the labelSelected to the value of the cell.
refToTableViewCell.labelSelected = youCell.textlabel?.text
If you set up a segue in storyboards from one storyboard to another, you can use the code below in your prepareForSegue method. You'll need to add a testFromMenuTableViewController property to your TypeItemsTableViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? TypeItemsTableViewController,
let path = self.tableView.indexPathForSelectedRow,
let cell = self.tableView.cellForRow(at: path),
let text = cell.textLabel?.text {
destination.textFromMenuTypeTableViewController = text
}
}
For more info check this SO answer.

how to perform segue to a VC with Container

see this gif
when I choose the city Med , it passed to the TableVC not to the FirstVC (MainVC)
can I do that ? segue to the mainVC with the data passed through
the container (TableVC) ?
here what I did so far
MainVC
Empty
TableVC
import UIKit
class passedViewController: UITableViewController {
#IBOutlet weak var passcelltow: UITableViewCell!
#IBOutlet weak var passcell: UITableViewCell!
var passedCity1 = "اختر المدينة الاولى"
var passedCity2 = "اختر المدينة الثانية"
override func viewDidLoad() {
super .viewDidLoad()
passcell.textLabel?.text = passedCity1
passcelltow.textLabel?.text = passedCity2
}
}
Table 1 with data to pass to the TableVC
import UIKit
class city2ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
var city2 = ["RUH" , "Med" , "Jed"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return city2.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
print(indexPath.row)
cell.textLabel?.text = city2[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "show", sender: city2[indexPath.row])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let passing = segue.destination as! passedViewController
passing.passedCity2 = sender as! String
}
}
Table 2 is the same ..
commend error
0 1 2 Could not cast value of type 'UIViewController' (0x107a10288) to
'table_view_test_pass.passedViewController' (0x105dbfdf8). (lldb)
You can pass data via segues or protocols. Since you are using segues i will show you a complete example and how to do it the right way in Swift 3. Using only two ViewControllers.
Create two UITextFields in the main "ViewController".
Create a new view controller of type UIViewController call it "MainTabelViewController" and add a tableView in it. Select content Dynamic prototypes Style Grouped and create 1 prototype cell and add a UILabel to it for the city name. "Don't forget the put the cell identifier name". I called it "cell".
Add the delegates and data sources to the class and add its functions like in code.
Create a segue from the main view controller to the main table view controller. And create another segue the opposite direction. "Don't forget the put the segue identifier names" I called them "toCity" & "toMain"
Create a "CityTableViewCell" controller of type UITableViewCell and create an IBOutlet of UILabel type where you will save the city name in as a text.
Edit this part in the AppDelegate.swift To delete the city names saved using in the UserDefaults every time the app is launched. So i wont populate the UITextFields randomly every time.
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var userDefaults: UserDefaults!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
userDefaults = UserDefaults.standard
userDefaults.removeObject(forKey: "City One")
userDefaults.removeObject(forKey: "City Two")
return true
}
This is the ordinary main ViewController.swift where you have your UITextFields in. I distinguish which UITextField did the user click on using the tags. You need to add also the UITextFieldDelegate protocol to be able to use the the textFieldDidBeginEditing function. And i also save the selected city names using UserDefaults class to call them when user chooses the other city.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var cityOneLabel: UITextField!
#IBOutlet var cityTwoLabel: UITextField!
#IBOutlet var continueButton: UIButton!
var selectedCityOne = ""
var selectedCityTwo = ""
var userDefaults: UserDefaults!
override func viewDidLoad() {
super.viewDidLoad()
cityOneLabel.delegate = self
cityTwoLabel.delegate = self
cityOneLabel.tag = 1
cityTwoLabel.tag = 2
continueButton.isEnabled = false
}
override func viewDidAppear(_ animated: Bool) {
userDefaults = UserDefaults.standard
cityOneLabel.text = selectedCityOne
cityTwoLabel.text = selectedCityTwo
if selectedCityOne != "" {
userDefaults.set(selectedCityOne, forKey: "City One")
} else {
cityOneLabel.text = userDefaults.string(forKey: "City One")
}
if selectedCityTwo != "" {
userDefaults.set(selectedCityTwo, forKey: "City Two")
} else {
cityTwoLabel.text = userDefaults.string(forKey: "City Two")
}
if cityOneLabel.text != "" && cityTwoLabel.text != "" {
continueButton.isEnabled = true
} else {
continueButton.isEnabled = false
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func continueButtonAction(_ sender: UIButton) {
//Later on continue after selecting the cities
}
func textFieldDidBeginEditing(_ textField: UITextField) {
performSegue(withIdentifier: "toCity", sender: textField.tag)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toCity" {
guard let cityVC = segue.destination as? MainTableViewController else {
return
}
cityVC.selectedTextField = sender as! Int
}
}
}
In the CityTabelViewCell.swift add the IBOutlet UILabel for the city name.
import UIKit
class CityTableViewCell: UITableViewCell {
#IBOutlet var cityNameLabel: 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
}
}
For the MainTabelViewController.swift write this:
Here is where i create an array of strings to populate my table view UILabels with.
import UIKit
class MainTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var cityTabelView: UITableView!
var cityNamesArray = ["Cairo", "Alexandria", "Suez"]
var selectedTextField = Int()
var selectedCityName = ""
override func viewDidLoad() {
super.viewDidLoad()
cityTabelView.delegate = self
cityTabelView.dataSource = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CityTableViewCell
cell.cityNameLabel.text = cityNamesArray[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cityNamesArray.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedCityName = cityNamesArray[indexPath.row]
performSegue(withIdentifier: "toMain", sender: self)
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
var title = ""
if selectedTextField == 1 {
title = "City One"
} else if selectedTextField == 2 {
title = "City Two"
}
return title
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMain" {
guard let mainVC = segue.destination as? ViewController else {
return
}
if selectedTextField == 1 {
mainVC.selectedCityOne = selectedCityName
} else if selectedTextField == 2 {
mainVC.selectedCityTwo = selectedCityName
}
}
}
}
This is how my layout looks like. Try it. I just added a continue button too if the user will have to go to another UIViewController after selecting the two cities.
If you want to segue to MainVC, you should instantiate a view controller from that class in prepare for segue.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let passing = segue.destination as! ViewController
passing.passedCity2 = sender as! String
}
Change ViewController to whatever the name of your class is for MainVC.
If you want to go back to the Parent View, you should be using an unwind-segue.
For that you must create the unwind segue method in the Parent View like this
#IBAction func unwindSegueFromChild(segue: UIStoryboardSegue){
// This code executes when returning to view
}
And in your child view you must create the unwind segue ctrl+dragging
There a dropdown appears and you select unwindSegueFromChild
Once you've done that, you must assign the unwind segue an identifier and programmatically perform it like a normal segue.

What is the missing command to carry the cell title from UITableView to new ViewController via this segue?

I have ViewController that contains a UITableView. Data is loaded into that table via the following code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UsernameSentDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var receiveUsername: UILabel!
#IBOutlet weak var userEmailText: UILabel!
var userEmail: String?
var communities = [String]() { didSet { communitiesTableView.reloadData()
}
}
var flag = false
#IBOutlet weak var communitiesTableView: UITableView!
#IBAction func unwindToHome(_ segue: UIStoryboardSegue) {
}
//recieves email address from delegate from LoginViewController
func userLoggedIn(data: String) {
userEmailText.text = data
}
override func viewDidLoad() {
super.viewDidLoad()
self.communitiesTableView.delegate = self
self.communitiesTableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.communities.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let title = self.communities[indexPath.row]
let cell = UITableViewCell()
cell.textLabel?.text = title
return cell
}
I then set up 1 prototype cell within the UITableView so I could create a segue to my second view controller, ShowCommunitiesViewController and named this segue, "showCommunitySegue"
In ShowCommunitiesViewController I have a label set up and ready to use as the title of the cell name carried across, named communityName.
In ViewController I have set up the following function to deal with the segue, including the destination variable for the cell title that has been selected.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "showCommunitySegue", sender: self)
showCommunityController.communityName = //what do I put here?
}
What do I need to put on that last line so showCommunityController.communityName displays the cell title?
Just declare selectedCellTitle as String in your viewController where your cells are.
var selectedCellTitle: String?
This will be the global variable keeping track of the selected cell's title.
Add the following in didSelectRowAt:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Set your global variable to the title
self.selectedCellTitle = self.communities[indexPath.row]
// Trigger your segue
performSegue(withIdentifier: "showCommunitySegue", sender: self)
}
Override prepareforsegue method the following way:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showCommunitySegue" {
// Check if the segue's destination viewcontroller is your viewcontroller
if let showCommunityController = segue.destination as? ShowCommunityViewController {
// Assign the selected title to communityName
showCommunityController.communityName = self.selectedCellTitle
}
}
}

Correct way to publish model to delegated view?

I have a UITableViewController which provides an array of strings as data source:
import UIKit
class ItemsViewController: UITableViewController {
let items = [
"Bob",
"Joe",
]
#IBAction
func unwindToSegue(segue: UIStoryboardSegue) {
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Item", forIndexPath: indexPath) as UITableViewCell
cell.textLabel.text = items[indexPath.row]
return cell
}
}
I now added a second scene with a UIViewController which is activated when the user clicks on a UITableCell through a show segue.
I would now like to take the string assigned with the former UITableCell and inserted into the ItemViewController:
import UIKit
class ItemViewController: UIViewController {
#IBOutlet
weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//var name = segue.someMethodHowIGetTheDataAssignedToTableCell
//nameLabel.text = name
}
}
How do I pass the model (in this case the string) assigned to the single TableCell over to the new ItemViewController?
Like this. Just add a return push segue from ItemViewController back to ItemsViewController. Name them less similar in the future :-)
import UIKit
class ItemsViewController: UITableViewController {
let items = [
"Bob",
"Joe",
]
#IBAction
func unwindToSegue(segue: UIStoryboardSegue) {
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Item", forIndexPath: indexPath) as UITableViewCell
cell.textLabel.text = items[indexPath.row]
return cell
}
//Passing details to detail VC
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = self.tableView.indexPathForSelectedRow()
let theDestination = (segue.destinationViewController as ItemViewController)
theDestination.itemName = items[indexPath!.row]
}
}
import UIKit
class ItemViewController: UIViewController {
#IBOutlet weak var nameLabel: UILabel!
var itemName = ""
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = itemName
}
}

Resources