how to modify an array from another class - ios

Im making to do list-app which consist of 2 View Controllers
one with table view hold an array and display it and second one with a textField, a button function to append the text field's text to the array
to display the new appended string it in the first view controller
heres my code:
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var exmpArray = ["DDDD","rrr","TTT"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func addBtnBar(_ sender: Any) {
performSegue(withIdentifier: "showMe", sender: nil)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return exmpArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = exmpArray[indexPath.row]
return cell!
}}
and the second one is:
class SecondViewController: UIViewController {
#IBOutlet weak var myTextField: UITextField!
var realAry:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
let myObj = ViewController()
#IBAction func addBtn(_ sender: Any) {
myObj.exmpArray.append(myTextField.text!)
print(myObj.exmpArray)
}
after appending the new words it doesn't display in the first controller

One problem is this line:
let myObj = ViewController()
That creates a new instance of ViewController that has nothing to do with the view controller that called you. Get rid of that line. It's wrong.
You need to set up a delegate property in SecondViewController that points back to ViewController, define a protocol for that delegate to conform to, and use that protocol so that SecondViewController can notify it's delegate when the array changes.
In ViewController, add a prepareForSegue() function that takes the destination view controller, casts it to type SecondViewController, and sets SecondViewController's delegate to self.

Both arrays are different you are just copying first VC array to 2nd VC array. Array is value type in swift. Instead of creating 2 arrays make 1st VC array as static.
static var exmpArray = ["DDDD","rrr","TTT"]
Now you can use & update this in all VC by calling like below
FirstVC.exmpArray

Related

UITableView Not Loading Data From Segue

I have a sequence where the user logs in and then they are brought to view showing a UITableView via a segue. I am trying to inject data from the login screen to the table after a successful login.
In the login view...
func transitionToHome() {
print("Hey you logged in!")=
performSegue(withIdentifier: "loginSuccess", sender: self)
self.navigationController!.setNavigationBarHidden(false, animated: false)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ViewController
vc.models = [(title: "title", note: "Note")]
}
And in the Home Screen view
#IBOutlet var table: UITableView!
#IBOutlet var label: UILabel!
#IBOutlet weak var newNoteButton: UIButton!
var models: [(title: String, note: String)] = []
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
title = "Notes"
}
I have tried to call table.reloadData() in the the viewDidLoad, as well as in viewDidAppear and viewWillAppear. Neither has worked.
I also have printed out models in the viewDidLoad and I saw that the data is being correctly passed to the view controller. But,I cannot get the table to load this data when the view controller is loaded from the segue.
If you models data prints correctly in viewDidLoad() you should be able to configure a tableview cell with the data in the dataSource implementation. See sample extension for the dataSource implementation below. Make sure you provide a reuse identifier to your prototype cell in the storyboard.
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
var content = cell.defaultContentConfiguration()
content.text = "This is a cell primary text"
cell.contentConfiguration = content
return cell
}

Add items to another view controller table view on button click

I have a view controller called ListViewController and another called AddFoodViewController. In ListViewController users are able to add their own ingredients to the grocery list which is presented in a table view. When they go to the AddFoodViewController, users should be able to click a button which says "add to list" which will add the array of ingrediets (that are already presented in a table view) into the grocery list. I am new to this, so I was wondering if anyone can help? I have successfully been able to get the ListViewController to work, however I am not sure how to add the array of ingredients from AddFoodViewController into the previous ListViewController.
class AddFoodViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var FoodTableView: UITableView!
#IBOutlet weak var sendFoodBtn: UIButton!
//array of food
let array = ["1 Salad", "3oz Chicken", "2 Tomatoes", "2 Cucumbers"]
let category = ""
override func viewDidLoad() {
super.viewDidLoad()
}
//display array in table view
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return(array.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let foodCell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "foodCell")
foodCell.textLabel?.text = array[indexPath.row]
foodCell.backgroundColor = .clear
foodCell.textLabel?.textColor = .darkGray
foodCell.textLabel?.font = UIFont(name: (foodCell.textLabel?.font.fontName)!, size:17)
return foodCell
}
//button that is supposed to add all ingredients to the ListViewController
#IBAction func addOnClick(_ sender: Any) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "toList"){
let vc = (segue.destination as! ListViewController)
vc.category = array
}
}
}
Passing data to previous view controller can be implemented by delegation pattern, first at all, declare a protocol in your AddFoodViewController, and define a delegate property in view controller.
// AddFoodViewController.swift
protocol AddFoodViewControllerDelegate {
func addIngredient(array: [String])
}
class AddFoodViewController: UIViewController {
...
var delegate: AddFoodViewControllerDelegate?
// MARK: add function
func actionAdd() {
delegate?addIngredient(array)
}
...
}
Back to your ListViewController,find the segue destination view controller which is your AddFoodViewController (remember to assign its class name in the storyboard), and assign delegate to self.
// ListViewController.swift
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
...
let vc = segue.destination as? AddFoodViewController
vc?.delegate = self
...
}
// in the same file, implement delegate method here
extension ListViewController: AddFoodViewControllerDelegate {
func addIngredient(array: [String]) {
items += array
// do table reload or something
}
}

how to make sure that my for in loop enables me to perform only one segue?

I have a tableview inside a view controller, that is connected to 3 different view controller by a segue. Each one has an identifier.
In order to perform a segue to the corresponding view controller, I have created an array that contains the identifiers for each segue. like so :
var tableauSegues = ["versChapitre1", "versChapitre2", "versChapitre3"]
In order to manage the segue according to the index of the array, I chose to use a for in loop inside of an iB action, like this:
#IBAction func versHistoire(_ sender: UIButton) {
for i in tableauSegues {
performSegue(withIdentifier: i, sender: self)
}
}
the problem is that all 3 segues are performed. I only want to perform one segue at the time.
any idea ?
Obviously, you want to avoid looping, but instead want to just use tableauSegues[row]. But the trick is how to get the row.
Bottom line, you should let the cell handle the tapping of the cell, and have the cell tell the table view controller when its button was tapped. But the trick is that when it does that, it should supply a reference to itself when it does that, so that the view controller can determine which row had its button tapped.
Thus, remove your existing #IBAction directly between the button and the view controller, and instead, hook it up to an #IBAction in the cell’s base class.
// CustomCell.swift
import UIKit
protocol CustomCellDelegate: class {
func didTapButton(_ sender: Any, in cell: CustomCell)
}
class CustomCell: UITableViewCell {
#IBOutlet weak var customLabel: UILabel!
static let preferredReuseIdentifier = "CustomCell"
weak var delegate: CustomCellDelegate?
func configure(text: String, delegate: CustomCellDelegate) {
customLabel.text = text
self.delegate = delegate
}
#IBAction func didTapButton(_ sender: Any) {
delegate?.didTapButton(sender, in: self)
}
}
Note, when I configure that cell, I’m going to have the table view provide not only whatever values need to be displayed in the cell, but also indicate that it is the delegate for that cell:
// ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var tableauSegues = ["versChapitre1", "versChapitre2", "versChapitre3"]
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableauSegues.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CustomCell.preferredReuseIdentifier, for: indexPath) as! CustomCell
cell.configure(text: "...", delegate: self)
return cell
}
}
extension ViewController: CustomCellDelegate {
func didTapButton(_ sender: Any, in cell: CustomCell) {
let row = tableView.indexPath(for: cell)!.row
let identifier = tableauSegues[row]
performSegue(withIdentifier: identifier, sender: self)
}
}

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.

I cannot get the tabbar delegate to call

I have asked this problem b4 and have tried for so long...
Problem: I have 4 tabs, when the third one is selected i want to wait until the tab has been changed, then send info to a Stringbuilder etc. But I can never seem to get it to call properly, I have tried delegating the genreViewController and tried to use the protocols but it never calls...
Please tell me what I need to put inside of the tab bar function since I do not know what should be there. I have tried putting the tabbarcontroller there and select viewcontroller. I am very new to all this so please dont be harsh :(
TabBarController:
import UIKit
class TabBarViewController: UITabBarController{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tabBar(_ tabBar: TabBarViewController, didSelect item: UITabBarItem) {
print("he")
//ask where it is first tab bar item
if self.tabBarController?.selectedIndex == 1 {
print("genres")
}
}
}
GenreViewController:
import UIKit
class GenreViewController: UIViewController, UITableViewDelegate,UITableViewDataSource, UITabBarControllerDelegate {
#IBOutlet weak var genreSwitch: UISwitch!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//self.tabBarController?.delegate = self
tableView.isScrollEnabled = false
let tabBarViewController = TabBarViewController()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
let genreArray = ["Action","Adventure","Comedy","Fantasy","Drama","Horror","Romance","Thriller","Family"]
let genreIds = [28,12,35,14,18,27,10749,53,10751]
//LATER!!: Save this Array until next time they log into the app
var activeGenreArray = [Int!](repeating: 0, count: 9)
//Number of rows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return genreArray.count
}
//What to do with tableview
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! GenreCustomcell
cell.genreSwitch.isEnabled = true
cell.genreName.text = self.genreArray[indexPath.row]
cell.genreSwitch.setOn(false, animated: true)
cell.genreSwitch.addTarget(self, action: #selector(GenreViewController.switchChange(sender:)), for: UIControlEvents.valueChanged)
cell.genreSwitch.restorationIdentifier = "\(genreIds[indexPath.row])"
cell.genreSwitch.tag = indexPath.row
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func switchChange(sender: UISwitch){
let id = Int(sender.restorationIdentifier!)
let row = Int(sender.tag)
print(row)
if sender.isOn == true{
activeGenreArray[row] = id!
print(activeGenreArray)
}
else{
activeGenreArray[row] = 0
print(activeGenreArray)
}
}
}
class GenreCustomcell: UITableViewCell{
#IBOutlet weak var genreName: UILabel!
#IBOutlet weak var genreSwitch: UISwitch!
}
It's not clear to me what you are trying to do here. You've declared your GenreViewController to be a UITabBarControllerDelegate but then implemented a UITabBarControllerDelegate method (tabBar:didSelect:) on your TabBarViewController subclass of UITabBarController.
I would expect to see you have:
a reference to some UITabBarController (this could be more specifically a TabBarViewController)
a reference to some UITabBarControllerDelegate (this could be a GenreViewController, a TabBarViewController if it should be its own delegate, or some other object entirely)
to set the UITabBarController instance's delegate property to be the UITabBarControllerDelegate instance.
Things that may have confused you:
A view controller can exist (and be associated with a tab) but does
not load it's view until it is needed (i.e. you are switching to its
tab). If you added your GenreViewController to a tab bar controller
it's viewDidLoad method will not be called until the tab bar
controller switches to that tab the first time. If your
GenreViewController was setting its own tab bar controller's
delegate in viewDidLoad (e.g. self.tabbarController?.delegate =
self) it may not become the tab bar controller's delegate when you
expected and so would not be notified of changes in the selected tab
until after it had been selected once.
It's not clear what the let tabBarViewController =
TabBarViewController() line is meant to do but that is creating a
new TabBarViewController which is not the one you might already be
seeing in your window. Creating a new instance of a class is not equivalent to referencing some existing instance of that same class.

Resources