pass two variables to the same view Controller swift - ios

I want to pass two different data form two different View Controller (Table1VC - Table2VC) to one distention (MainVC)..
I did that and its worked .. but the problem is :
when I pass the first data there is no problem here
BUT the problem is when I pass the second data the first one is disappear !!
this is what I did so far :
Table1VC :
import UIKit
class Table1ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource{
#IBOutlet weak var tableview: UITableView!
var city = ["data1" , "data2" , "Data3"]
var passit = ""
override func viewDidLoad() {
super.viewDidLoad()
tableview.delegate = self
tableview.dataSource = self
self.navigationController?.navigationBar.tintColor = UIColor.orange
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return city.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = city[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
passit = city[indexPath.row]
performSegue(withIdentifier: "showtable1", sender: city[indexPath.row])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
let passData1 = segue.destination as! MainVC
passData1.passcity1 = passit
}
}
Table2VC
import UIKit
class passVC: UIViewController , UITableViewDelegate , UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
var city2 = ["data1" , "data2" , "Data3"]
var passit = ""
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
self.navigationController?.navigationBar.tintColor = UIColor.orange
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return city2.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = city2[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
passit = city2[indexPath.row]
performSegue(withIdentifier: "showtable2", sender: city2[indexPath.row])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
let passData2 = segue.destination as! MainVC
passData2.passcity2 = passit
}
}
MainVC
import UIKit
class MainVC: UIViewController {
#IBOutlet weak var passText1: UITextField!
#IBOutlet weak var passText2: UITextField!
var passcity1 = ""
var passcity2 = ""
override func viewDidLoad() {
super .viewDidLoad()
passText1.text = passcity1
passText2.text = passcity2
}
}
the passed data will be in textfield in MainVC

This is kind of like the basics of he view stack and the life cycle of the view problem.
When you type the line:
let passData1 = segue.destination as! MainVC
it means you want to make one instance of the MainVC and want to fill up that object's data.
The instant you segue to that controller and come back to the main controller, you have lost the previous object that had the 1st value.
Example:
From VC1:
let passData1 = segue.destination as! MainVC -> means telling the compiler make me an object name "passData1", and after this any operation done on passData1 will affect passData1 only
From VC2:
let passData2 = segue.destination as! MainVC -> means telling the compiler make me an object name "passData2", and after this any operation done on passData2 will affect passData2 only.
All in all, your objects have changed, hence the previous state of the object is lost. The instant you navigate out of the MainVC for the 1st time, the next time any MainVC object is made, its a fresh copy of MainVC. You will need to set value1 and value2, both.
If you want to maintain the value of passData1, then save the required values somewhere, and when passing passData2, get those values and pass the complete set.

Related

Allowing the user to create a tableViewCell with text from another viewController?

I'm creating an app, in which one of the functions is, that the user should be able to write a person's name and an answer to a question - and then when pressing the save-button he/she should be redirected to the previous controller again, which not have created a tableViewCell with this data as title. (Later on you can ofcourse click this cell and see the data in third viewcontroller.)
My way of tackling this was to let the "save" button save the name and the answer by using NSUserDefault. Then connecting a segue to the button at the same time to make it redirect the user to the previous controller - and finally to have the tableView in the previous controller refer to the newly created NSUserDefault-key in the cell.textfield.
I have two questions.
Why does this not work? My code from both viewControllers are underneeth. I don't get why it doesn't work.
If I do get this to work: How do I implement the effect, that every time you enter the "Creating viewController", in which you can write the name and the answer - the user gets the option of saving a NEW person and adding a NEW cell, instead of overriding the old one, which I'm afraid will happen if I get the current approach to work...
Code in the "Creating viewController", where you can write the name and the answer:
class CreateNewPerson: UIViewController {
let defaults = UserDefaults.standard
#IBOutlet weak var Question: UILabel!
#IBOutlet weak var ExtraIdentifier: UILabel!
#IBOutlet weak var PersonName: UITextField!
#IBOutlet weak var PersonAnswer: UITextField!
#IBOutlet weak var PersonExtraIdentifier: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
showDiaryIdentifiers () }
func showDiaryIdentifiers () {
let DiaryQuestion = self.defaults.string(forKey: "DiaryQuestionKey")
let ExtraIdentifer = self.defaults.string(forKey: "RandomIdentifierKey")
self.Question.text = DiaryQuestion
self.ExtraIdentifier.text = ExtraIdentifer
}
#IBAction func SavePerson () {
self.defaults.setValue(self.PersonName.text, forKey: "PersonNameKey")
self.defaults.setValue(self.PersonAnswer.text, forKey: "PersonAnswerKey")
self.defaults.setValue(self.PersonExtraIdentifier.text, forKey: "PersonExtraIdentiferKey")
} }
Code in the other viewController:
class AllPersonsInYourDiary: UIViewController, UITableViewDelegate, UITableViewDataSource {
let defaults = UserDefaults.standard
#IBOutlet weak var ShowingDiaryName: UILabel!
#IBOutlet weak var ShowingDiaryQuestion: UILabel!
#IBOutlet weak var ShowingExtraIdentifer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
showDiaryIdentifiers()
self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showDiaryIdentifiers () {
let DiaryName = self.defaults.string(forKey: "DiaryNameKey")
let DiaryQuestion = self.defaults.string(forKey: "DiaryQuestionKey")
let ExtraIdentifer = self.defaults.string(forKey: "RandomIdentifierKey")
self.ShowingDiaryName.text = DiaryName
self.ShowingDiaryQuestion.text = DiaryQuestion
self.ShowingExtraIdentifer.text = ExtraIdentifer
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Person1", for: indexPath)
cell.textLabel?.text = self.defaults.string(forKey: "PersonNameKey")
cell.textLabel?.numberOfLines = 0
cell.textLabel?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
return cell
}
In this code, I guess what is not working is the cellForRowAt method. What am I getting wrong? Right now it's not creating any cells at all.
Also, I know I should notr1 return 1 row and 1 section. It's just for now. I know I should in the end return Something.count - but I haven't yet figured out what this something is...
Thanks!
You already created a table view with only one row.
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
When returning to previous controller just reload tableview like(Make sure before reloading datasource have contain new data.)
tableView.reloadData()
If I understand correctly that you need the user to enter a set of values and then use these values to populate a table view in another view controller, then what you wanna do is:
1- create 2 dictionaries, an optional dictionary in AllPersonsInYourDiary that would carry the new values and one in your CreateNewPerson something like this let dic = [[String: String]]().
2- Instantiate the view controller:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "#yourSegueIdentifier" {
let vc = segue.destination as! AllPersonsInYourDiary
vc.dic = self.dic
}
}
3- in your AllPersonsInYourDiary view controller, override the functions like this:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dic.count
}
and populate the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Person1", for: indexPath)
cell.textLabel?.text = dic[indexPath.row]["#whateverKeyForValue"]
cell.textLabel?.numberOfLines = 0
cell.textLabel?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
return cell
}

Can't pass data via segue

I make app with news feed which has to open on other ViewController. But can't pass data via segue.
Viewcontroller with newsfeed
class SecondViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var titlenews = ""
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "newsfeedCell", for: indexPath) as! NewsFeedCell
cell.newsfeed_title.text = self.news?[indexPath.item].headline
cell.newsfeed_topic.text = self.news?[indexPath.item].topic
cell.newsfeed_time.text = timetime(from: (self.news?[indexPath.item].time)!)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("tableview")
let vc = storyboard?.instantiateViewController(withIdentifier: "newsBody") as? NewsBody
vc?.labeltext = (self.news?[indexPath.item].headline)!
print((self.news?[indexPath.item].headline)!)
self.navigationController?.pushViewController(vc!, animated: true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.news!.count
} //number of rows
#IBOutlet weak var tableview: UITableView!
var news: [Newsfeed]? = []
override func viewDidLoad() {
super.viewDidLoad()
getJSON()
}
func getJSON(){
///Here all do right
}
}
Viewcontroller which has to receive data from news feed
class NewsBody: UIViewController {
#IBOutlet weak var testLabel: UILabel!
var labeltext = ""
override func viewDidLoad() {
super.viewDidLoad()
print(labeltext)
testLabel.text = labeltext
}
}
print(labeltext) shows that NewsBody receive empty value or nothing.
But print((self.news?[indexPath.item].headline)!) inside of SecondViewController shows that I try to push proper value.
What I do incorrect between this actions? What wrong with segue and pass of data?
It seems that instantiateViewController(withIdentifier: "newsBody") triggers view load under the hood. It should not (in theory) but it might do just that in your case.
This means that viewDidLoad() will be called before the vc?.labeltext = (self.news?[indexPath.item].headline)! is executed.
I'd recommend you to do the following.
class NewsBody: UIViewController {
#IBOutlet weak var testLabel: UILabel!
var labeltext: String? {
didSet { updateUI() }
}
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
private func updateUI() {
testLabel.text = labeltext
}
}
This way if you set the labeltext property after the view is loaded, it will still trigger the UI update. And if you set the labeltext property before the view is loaded, as soon as viewDidLoad() is called.
BTW, you are not using segues here. But even if you do, you can easily use the same method as I proposed, because it allows you to stop thinking about whether property updates will update the UI.
Also please note that I made the property optional. It will allow you to avoid force casts and just do
vc?.labeltext = self.news?[indexPath.item].headline
UILabel.text is also an optional String property, so they will play well together.

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.

How to reload a view-controller after data has been fetched from a network request?

I have a problem and can't seem to fix it after looking at tutorials online and other SO questions with a similar problem, which leaves me to think I've done something wrong/bad practice related in my code.
I have 2 table view controllers.
The first TableViewController is populated from a database, all this works fine. When I click one of the cells it segues to a second TableViewController which also should be populated from a database (depending on what you select in the first VC).
Currently if I click a cell in TVC1 it goes to TVC2 and it's empty, then it I click back within my navigation controller and select something else, it goes back to TVC2 and shows me my first selection. This indicates that TVC2 is being loaded before the network has returned its data from the database.... so, I tried using tableView.reloadData() in various places like viewDidLoad and viewDidAppear, but i just can't seem to get it to work.
Below is both TVC's. I've stuck with MVC design pattern and haven't included the model and severConnection code for each TVC because I don't want to over complicate the post, however if you'd like to see either I will update.
Thanks in advance for any help.
TableViewController1
class MenuTypeTableViewController: UITableViewController, MenuTypeServerProtocol {
//Properties
var cellItems: NSArray = NSArray()
var selectedItem = String()
override func viewDidLoad() {
super.viewDidLoad()
let menuTypeServer = MenuTypeServer()
menuTypeServer.delegate = self
menuTypeServer.downloadItems()
}
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
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath)
selectedItem = (selectedCell?.textLabel?.text)!
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "typeItems" {
let destinationVC = segue.destination as? TypeItemsTableViewController
destinationVC?.selectedItem = self.selectedItem
}
}
}
TableViewController2:
class TypeItemsTableViewController: UITableViewController, TypeItemsServerProtocol {
//Properties
var cellItems: NSArray = NSArray()
var selectedItem: String = String()
let typeItemsServer = TypeItemsServer()
override func viewDidLoad() {
super.viewDidLoad()
typeItemsServer.delegate = self
self.typeItemsServer.foodType = self.selectedItem
self.typeItemsServer.downloadItems()
self.tableView.reloadData()
}
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
}
}
Try adding this to TypeItemsTableViewController
override func viewDidLoad() {
super.viewDidLoad()
cellItems = NSArray()//make sure you have the empty array at the start
typeItemsServer.delegate = self
self.typeItemsServer.foodType = self.selectedItem
self.typeItemsServer.downloadItems()
self.tableView.reloadData()
}
and
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
typeItemsServer.delegate = nil
}
Add this at the top
var cellItems: NSArray = NSArray() {
didSet {
tableview.reloadData()
}
}
Now you can remove other tableview.reloadData() calls since it will automatically be called once cellItems are set...
I think you have a timing problem. You're reloading right after your async data call. You reload but your data isn't in place at that time. Try using functions with escaping or use "didSet" on your data like:
var dataArray: [type] {
didSet {
tableview.reloadData()
}
}

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
}
}
}

Resources