Call function in secondFile.swift/viewController from firstFile.swift/viewController? - ios

I'm trying to call a function() made in my first ViewController from another function() made in the second ViewController.
It's a function to update the title of a button in the firstViewController.
I have searched but I can't find a way.
First ViewController // ViewController.swift
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
weightLabel.delegate = self
}
#IBAction func excerciseChooserButton(_ sender: UIButton) {
}
var weight = 0 {
didSet {
weightLabel.text = "\(weight)"
}
}
// User input WEIGHT
#IBOutlet weak var weightLabel: UITextField!
func textField(_ weightLabel: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let isNumber = CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string))
let withDecimal = (
string == NumberFormatter().decimalSeparator &&
weightLabel.text?.contains(string) == false
)
return isNumber || withDecimal
}
#IBAction func plusWeight(_ sender: UIButton) {
weight += 5
}
#IBAction func minusWeight(_ sender: UIButton) {
weight -= 5
}
// User input REPS
#IBOutlet weak var repLabel: UILabel!
#IBAction func repSlider(_ sender: UISlider) {
let currentRepValue = Int(sender.value)
repLabel.text = "\(currentRepValue)"
let cm = Calculator(weight: weightLabel.text!, reps: repLabel.text!)
let result = cm.calcRM()
repMax.text = "1RM: \(result)kg"
}
#IBOutlet weak var repMax: UILabel!
#IBOutlet weak var excerciseLabel: UIButton!
func changeText() {
excerciseLabel.setTitle(Excercises.excChosen, for: .normal)
print(excerciseLabel)
}
#IBAction func unwindToViewController(segue:UIStoryboardSegue) {
}
}
// // // //
Second ViewController // ExcerciseChooserViewController.swift
import UIKit
struct Excercises {
static var excChosen:String? = ""
}
class ExcerciseChooserViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
// Data model: These strings will be the data for the table view cells
let excercises: [String] = ["Bench Press", "Squat", "Push Press", "Deadlift"]
// cell reuse id (cells that scroll out of view can be reused)
let cellReuseIdentifier = "cell"
// don't forget to hook this up from the storyboard
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Register the table view cell class and its reuse id
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
// (optional) include this line if you want to remove the extra empty cell divider lines
// self.tableView.tableFooterView = UIView()
// This view controller itself will provide the delegate methods and row data for the table view.
tableView.delegate = self
tableView.dataSource = self
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.excercises.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
// set the text from the data model
cell.textLabel?.text = self.excercises[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let excerciseChosen = "\(excercises[indexPath.row])"
print("You tapped cell number \(indexPath.row).")
print(excerciseChosen)
goBackToOneButtonTapped((Any).self)
Excercises.excChosen = excerciseChosen
print(Excercises.excChosen!)
// call function to update text
ViewController.changeText()
}
#IBAction func goBackToOneButtonTapped(_ sender: Any) {
performSegue(withIdentifier: "unwindToViewController", sender: self)
}
}

Call it from unwindToViewController instead, no need to call it while the first view controller is not visible

There are many ways to do this, but I'll describe a simple one here.
Because you're going back to 'ViewController' via a segue, a good option for you is to override prepare(for:sender:). This will give you a reference to the destination view controller of that segue, which will then allow you to call functions or set properties in that view controller. You can read more about this method here.
Here are some basic steps:
In ViewController, update your changeText() method to accept a string parameter: changeText(_ text: String?).
Add a property to ExcerciseChooserViewController to hold the text you want to use: private var chosenExercise: String?
In your tableView:DidSelectRowAtIndexPath: method, set your new chosenExercise property to the string you want to pass to ViewController.
In prepare(for:sender:) of ExcerciseChooserViewController, grab a reference to destination view controller, downcast it to your subclass ViewController, and call your new method passing in the exerciseText string.
For example:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var excerciseLabel: UIButton!
func changeText(_ text: String?) {
guard let text = text else { return }
excerciseLabel.setTitle(text, for: .normal)
print(excerciseLabel)
}
}
And in ExcerciseChooserViewController:
class ExcerciseChooserViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private var chosenExercise: String?
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let excerciseChosen = "\(excercises[indexPath.row])"
print("You tapped cell number \(indexPath.row).")
print(excerciseChosen)
goBackToOneButtonTapped((Any).self)
Excercises.excChosen = excerciseChosen
print(Excercises.excChosen!)
chosenExercise = excerciseChosen
}
#IBAction func goBackToOneButtonTapped(_ sender: Any) {
performSegue(withIdentifier: "unwindToViewController", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationVC = segue.destination as? ViewController {
destinationVC.changeText(chosenExercise)
}
}
}

Related

TableView is not appending new data

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.

Loop through a uitableviewcell on submit in swift 4

I am trying to access each value of a text field in a prototype cell within a UITableView on Submit. I know I should be doing this in a better way (model) but for now, I just need to access these fields and cannot find a way to do this in Swift 3/4. Would anyone be able to assist?
Code:
import UIKit
import Firebase
class FormTableViewController: UITableViewController {
var formLabels = [String]()
var formPlaceholders = [String]()
override func viewDidLoad() {
super.viewDidLoad()
FirebaseApp.configure()
formLabels = ["Name","Email","Password", "Phone"]
formPlaceholders = ["John Smith","example#email.com","Enter Password", "8585551234"]
tableView.estimatedRowHeight = 30
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return formLabels.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier:
"FormTableCell", for: indexPath)
as! FormTableViewCell
let row = indexPath.row
cell.formLabel.font =
UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
cell.formLabel.text = formLabels[row]
cell.formTextField.placeholder = formPlaceholders[row]
return cell
}
#IBAction func submitButtonPressed(_ sender: Any) {
// Need to do something with the Name, Email, Phone and Password fields here
}
}
You seem to acknowledge that updating the model directly probably makes sense. So why not do that? Just:
Have model collection for the responses;
Set up delegate for the text field in the cell;
Have cellForRowAt set that delegate; and
Make the table view controller conform to that class.
So, something quick and dirty, set up the cell to hook up editChanged event from the text field and set up protocol to inform the view controller:
protocol FormTableViewCellDelegate: class {
func fieldValueChanged(cell: UITableViewCell, textField: UITextField)
}
class FormTableViewCell: UITableViewCell {
weak var delegate: FormTableViewCellDelegate?
#IBOutlet weak var formLabel: UILabel!
#IBOutlet weak var formTextField: UITextField!
#IBAction func editingChanged(_ sender: UITextField) {
delegate?.fieldValueChanged(cell: self, textField: sender)
}
}
And then have the view controller set up model object and conform to your new protocol:
class FormTableViewController: UITableViewController {
var formLabels = [String]()
var formPlaceholders = [String]()
var values = [String?]()
override func viewDidLoad() {
super.viewDidLoad()
...
formLabels = ["Name","Email","Password", "Phone"]
formPlaceholders = ["John Smith","example#email.com","Enter Password", "8585551234"]
values = [nil, nil, nil, nil]
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FormTableCell", for: indexPath) as! FormTableViewCell
let row = indexPath.row
cell.formLabel.font = .preferredFont(forTextStyle: .headline)
cell.formLabel.text = formLabels[row]
cell.formTextField.placeholder = formPlaceholders[row]
cell.formTextField.text = values[row]
cell.delegate = self // set the delegate, too
return cell
}
#IBAction func submitButtonPressed(_ sender: Any) {
print(#function, values)
}
}
// delegate protocol to update model as text fields change
extension FormTableViewController: FormTableViewCellDelegate {
func fieldValueChanged(cell: UITableViewCell, textField: UITextField) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
values[indexPath.row] = textField.text
}
}
Then that's it, your model is updated as the text fields are updated. Plus this has the advantage that it now supports cell reuse, conforms to MVC patterns, etc.
If you want to just loop through cells, you can create an array of ‘IndexPath’.
let array = (0..<formLabels.count).map { IndexPath(row: $0, section:0) }
After that you can loop over this array and access individual cell using tableview method:- tableView.cellForIndexPath
Hope this helps. (Not on my laptop, so didn’t test the syntax)

TableView Only Updates after Screen Rotates

Image of the tableview
I have a tableview with a collection view in each cell, all linked to an array. Each collection view has tags, so when I have stuff in the array from the beginning, all tableview cells and collection view cells appear properly in the app. But when I add an element to the array in the app itself (I have a second view controller with the stuff to do that), it works but the new table view cell only appears after the screen rotates (really odd). I have tried adding an object of the view controller with the table view in the second view controller where I add an element to the array. Then in the second view controller in ViewWillDisappear, I reloadData() through that object like this:
var vc : ViewController? = ViewController()
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
vc?.listOfActs.reloadData()
}
But this results in an EXC_BAD_INSTRUCTION
Then I tried adding self.listOfActs.reloadData() in the prepareForSegue in the view controller with the table view just so that I could see that it at least refreshes the data at some point in time but even that doesn't work when I click on add scene a second time.
UPDATE: New MainViewController
This is the new first view controller with the table view. I renamed it and have implemented the method for adding to array and reloading. It kind of works if I use an if let on the reloadData but then I'm back to square one where it only updates when I rotate the screen. When I get rid of the if let so it can actually try to update the table view, it gives me a Fata error: unexpectedly found a nil while unwrapping.
class MainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//The Table View
#IBOutlet var AddActButton: UIButton!
#IBOutlet weak var listOfActs: UITableView!
var sectionTapped : Int?
var indexitemTapped : Int?
override func viewDidLoad() {
super.viewDidLoad()
listOfActs.delegate = self
listOfActs.dataSource = self
}
//Table View Functions
func numberOfSections(in tableView: UITableView) -> Int {
return actsCollection.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "actCell", for: indexPath) as! ActCell
cell.setCollectionViewDataSourceDelegate(self, forSection: indexPath.section)
return cell
}
//Add To Table View
func addObjects(appendage: Act) {
actsCollection.append(appendage)
if let shit = listOfActs {
shit.reloadData()
}
}
//Header Cell
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cellHeader = tableView.dequeueReusableCell(withIdentifier: "headerCell") as! HeaderCell
cellHeader.headerName.text = actsCollection[section].actName
return cellHeader
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
}
//Scene Collection in Act Cell
extension MainViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return actsCollection[collectionView.tag].actScenes.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "sceneCell", for: indexPath) as! SceneCell
cell.sceneTitle.text = actsCollection[collectionView.tag].actScenes[indexPath.item].sceneTitle
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
sectionTapped = collectionView.tag
indexitemTapped = indexPath.item
performSegue(withIdentifier: "showDetail", sender: self)
}
//Segue Prepare
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
let detailsVC = segue.destination as! SceneDetailController
detailsVC.textToAppearInSceneName = actsCollection[sectionTapped!].actScenes[indexitemTapped!].sceneTitle
}
}
}
UPDATE:New second view controller, the one that adds to the array.
class AddActController: UIViewController, UITextFieldDelegate {
#IBOutlet var sceneLiveName: UILabel!
#IBOutlet var sceneNameTextField: UITextField!
#IBOutlet var sceneDescriptionTextField: UITextField!
#IBOutlet var AddSceneButton: UIButton!
#IBOutlet var cardBounds: UIView!
var newName: String? = ""
#IBOutlet var cardShadow: UIView!
var shit = MainViewController()
override func viewDidLoad() {
super.viewDidLoad()
sceneNameTextField.delegate = self
AddSceneButton.alpha = 0.0
cardBounds.layer.cornerRadius = 20.0
cardShadow.layer.shadowRadius = 25.0
cardShadow.layer.shadowOpacity = 0.4
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 0.2){
self.AddSceneButton.alpha = 1.0
}
}
#IBAction func exitButton(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
#IBAction func addSceneButton(_ sender: UIButton) {
if newName == "" {
sceneLiveName.text = "Enter Something"
sceneNameTextField.text = ""
}
else {
let appendAct: Act = Act(actName: newName!, actTheme: "Action", actScenes: [Scene(sceneTitle: "Add Act", sceneDescription: "")])
shit.addObjects(appendage: appendAct)
dismiss(animated: true, completion: nil)
}
}
//MARK: textField
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text: NSString = (sceneNameTextField.text ?? "") as NSString
let resultString = text.replacingCharacters(in: range, with: string)
sceneLiveName.text = resultString
newName = String(describing: (sceneLiveName.text)!.trimmingCharacters(in: .whitespacesAndNewlines))
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
sceneNameTextField.resignFirstResponder()
return true
}
/*
// 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?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
Here is the class for the uitableviewcell that contains its own collection view.
class ActCell: UITableViewCell {
#IBOutlet fileprivate weak var sceneCollection: UICollectionView!
}
extension ActCell {
func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource & UICollectionViewDelegate>(_ dataSourceDelegate: D, forSection section: Int) {
sceneCollection.delegate = dataSourceDelegate
sceneCollection.dataSource = dataSourceDelegate
sceneCollection.tag = section
sceneCollection.reloadData()
}
}
And here is the model with the user's data including the acts and scenes.
struct Scene {
var sceneTitle: String
var sceneDescription: String
//var characters: [Character]
//var location: Location
}
struct Act {
var actName: String
var actTheme: String
var actScenes : [Scene] = []
}
var actsCollection : [Act] = [
Act(actName: "dfdsfdsfdsf", actTheme: "Action", actScenes: [Scene(sceneTitle: "Example Act", sceneDescription: "")])
]
Any help is greatly appreciated. Thank you to all.
So if I'm not mistaken I believe the viewDidLoad method gets call during screen rotations. So this explains why it update during so. Now to get it to update without rotating the device, I would add an observer in the notificationCenter to watch for any updates to the tableView then call a #selector to do the reloadData(). So here is an example of this. In the viewDidLoad method add
NotificationCenter.default.addObserver(self, selector: #selector(refreshTable), name: NSNotification.Name(rawValue: "load"), object: nil)
Then add the method refreshTable()
func refreshTable() {
listOfActs.reloadData()
}
This is basically how I handle keeping the tableView refreshed.
Well - viewDidLoad is loaded only for the first time controller loads his view (not sure about rotation).
If you really need - you can reload tableView in viewWillAppear - but I wouldn't do this.
Instead of
actsCollection.append(appendAct)
dismiss(animated: true, completion: nil)
create a method on the first controller like addObjectToList(appendAct) and in that method, just easily append object to your list array and reload tableView after adding.
You will be reloading tableView only when you really add something to your list and not every time controller appears, you also don't need notification observer.
EDIT - UPDATE
What is this?
if newName == "" {
sceneLiveName.text = "Enter Something"
sceneNameTextField.text = ""
}
else {
let appendAct: Act = Act(actName: newName!, actTheme: "Action", actScenes: [Scene(sceneTitle: "Add Act", sceneDescription: "")])
shit.addObjects(appendage: appendAct)
dismiss(animated: true, completion: nil)
}
I mean - what is shit.AddObjects? Shit is defined as tableView - but you have to call this method on instance of your controller.
Another thing - change your setup from sections == number of items with 1 row to be one section with number of rows == number of items. :)

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

Resources