I'm new in swift, and I've been searching this answer for weeks, but I still haven't found it:( What I'm building is a master-detail app. It is a reminders app.The user is able to edit those reminders in the detail view, but the problem comes when I try to pass the edited data back to the master view, and add it to an array of arrays that I use as the database. I've tried with an unwind segue, but I don't know how to add in the right place the data that I have passed back. I would really appreciate your help!
//MAIN VIEW
var addMoneyArray = [[Int]]()
class GonMainTableViewController: UITableViewController {
//MARK: - VARIABLES
var recivedData : Int?
//MARK. - IB OUTLETS
#IBOutlet var tableView1: UITableView!
#IBAction func unwindViewController(segue: UIStoryboardSegue){
if(segue.sourceViewController .isKindOfClass(GonDetailViewController))
{
let view2 : GonDetailViewController = segue.sourceViewController as! GonDetailViewController
recivedData = view2.passTxtFieldValue
}
}
enter code here
//VIEW2
class GonDetailViewController: UIViewController {
var passTxtFieldValue : Int?
#IBAction func addBtn(sender: AnyObject) {
var returnMoney = Int(addMoneyTxtField.text!)
returnMoney = passTxtFieldValue
let alertController = UIAlertController(title: "Awesome!", message: "Succesfully saved your data!", preferredStyle: .Alert)
let takePhotoAction = UIAlertAction(title: "OK", style: .Default) { (Alert) -> Void in
self.performSegueWithIdentifier("returnToMain", sender: self)
}
alertController.addAction(takePhotoAction)
presentViewController(alertController, animated: true, completion: nil)
}
Related
[SOLVED] As a beginner, this most likely isn't an optimal or clever solution, however it does what it needs to do. The data from the AddVocabViewController is passed back via this delegate and then inserts a new instance of Vocab into the array in the data model. Also the delegation in the prepare for segue function allowed the data to pass through.
class ViewController: UIViewController, passNewWordData{
func passDataBack(data: Vocab){
vocabBuilder.n1LevelVocab.insert(data, at: vocabBuilder.n1LevelVocab.startIndex)
self.dismiss(animated: true) {
let currentVocabNumber = vocabNumber
self.vocabBuilder.n1LevelVocab.swapAt(currentVocabNumber, self.vocabBuilder.n1LevelVocab.startIndex)
self.loadUIN1()
print(vocabNumber)
}
}
Original Question is as follows:
I have a class modal view controller that passes data back successfully but I want to insert this data type into an array.
My modal view controller is this:
import UIKit
protocol passNewWordData {
func passDataBack(data: Vocab)
}
//pass back the data using this protocol and calling it as a delegate
var vocabBuilder1 = VocabBuilder()
class AddVocabularyViewController: UIViewController {
#IBOutlet var modalView: UIView!
#IBOutlet weak var vocabTextField: UITextField!
#IBOutlet weak var hiraganaTextField: UITextField!
#IBOutlet weak var englishTranslationTextField: UITextField!
var delegate: passNewWordData?
//this is crucial! This is the link between both VCs.
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addNewWord(_ sender: UIButton) {
guard let vocabText = vocabTextField.text, vocabTextField.hasText else {
print("Error no data")
let ac = UIAlertController(title: "Missing Vocabulary Word!", message: "Please fill in the Vocabulary field", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .cancel))
present(ac, animated: true)
return
}
guard let hiraganaText = hiraganaTextField.text, hiraganaTextField.hasText else {
print("Error no hiragana entered")
let ac = UIAlertController(title: "Missing hiragana!", message: "Please fill in the Hiragana field", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .cancel))
present(ac, animated: true)
return
}
guard let englishTranslationText = englishTranslationTextField.text, englishTranslationTextField.hasText else {
let ac = UIAlertController(title: "Missing English Translation!", message: "Please fill in the English Translation field", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .cancel))
present(ac, animated: true)
print("Error no english translation entered.")
return
}
//better to have guard lets to handle the data so conditions are correct before passing the data..
let newWord = Vocab(vocabTitle: vocabText, vocabHiragana: hiraganaText, englishTranslation: englishTranslationText, noOfTimesSeen: 0)
delegate?.passDataBack(data: newWord)
print(newWord)
}
This prints out the data that's being sent back no problem but what I want to do is add this data to a data model file I made specifically to an array inside it.
struct VocabBuilder {
var n1LevelVocab = [Vocab(vocabTitle: "上達", vocabHiragana: "じょうたつ", englishTranslation: "Improvement", noOfTimesSeen: 0),
Vocab(vocabTitle: "乗り越える", vocabHiragana: "のりこえる", englishTranslation: "To Push Through", noOfTimesSeen: 0),
Vocab(vocabTitle: "耐久性", vocabHiragana: "たいきゅうせい", englishTranslation: "Durability", noOfTimesSeen: 0),]
Things I've tried:
I tried to make the extension to the the main view controller to receive the data but it won't append the data when I call the functions from the data model to add them.
extension ViewController: passNewWordData {
func passDataBack(data: Vocab) {
self.dismiss(animated: true)
self.vocabInfo.n1LevelVocab.append(data)
//vocabInfo is calling the VocabBuilder Struct.
//need to append this data to the newWord variable then send that back to the n1VocabArray in the dataModel.
print(data)
}
}
I tested out inside the data model appending a new Vocab type and it does append an extra item to the array there, but how do I get that data from my modal view controller all the way back to that struct? Would I need to create an extension to handle the data from the protocol in my data model itself? Most examples are just passing directly into the view controller.
Any help would be appreciated.
I have a parentVC that allows user to add by using actionsheet's choices
Expected Result :
when actionsheet presented, user choose the choices and the actionsheet will perform segue to childVC and childVC's label.text will become the chosen choice
My issue is I print out self.textInAS is exactly what the action.title, however, when segue performed, textFromAS become nil, so after some research I guess I lack of closure that required, however I am still new in Swift and I not sure how to properly perform a closure. Please provide example with code to help.
Many Thanks!!
/* parentVC */
var textInAS : String?
#IBAction func addBtnPressed(_ sender: UIBarButtonItem) {
let alert = UIAlertController(title: "Alert Title", message: "alert msg", preferredStyle: .actionSheet)
let actionA = UIAlertAction(title: "Choices A", style: .default) { (action) in
let chosenTitle = action.title
self.textInAS = choosenTitle
self.performSegue(withIdentifier: "goChildVC", sender: self)
}
let actionB = UIAlertAction(title: "Choices B", style: .default) { (action) in
let chosenTitle = action.title
self.textInAS = choosenTitle
self.performSegue(withIdentifier: "goChildVC", sender: self)
}
alert.addAction(actionA)
alert.addAction(actionB)
present(alert, animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "goChildVC") {
let destinationVC = segue.destination as! ChildTableViewController
destinationVC.textFromAS = self.textInAS
}
}
}
}
/* ChildVC */
#IBOutlet weak var label: UILabel!
var textFromAS: String?
override func viewDidLoad() {
super.viewDidLoad()
label.text = textFromAS
}
I am quite puzzled on how will I construct my codes regarding on how I will filter the selected array from a tableviewcell. The JSON below is the content of the tableview which displays like
[
{
"hospitalNumber": "00000001",
"patientName": "Test Patient",
"totalAmount": 1111.3
},
{
"hospitalNumber": "00000002",
"patientName": "Test Patient 2",
"totalAmount": 1312
},
{
"hospitalNumber": "00000003",
"patientName": "Test Patient 3",
"totalAmount": 475
}
]
The problem is how can I display the selected hospitalNumber and patientName in the next View Controller, which will display like
This is what my `PaymentDetailsViewController' have:
var patientList: [Patient]! {
didSet {
latestCreditedAmountTableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
getPatientList()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPatientPaymentDetailsVC" {
if let patientPaymentDetailsVC = segue.destination as? PatientPaymentDetailsViewController {
patientPaymentDetailsVC.isBrowseAll = self.isBrowseAll
if !isBrowseAll {
patientPaymentDetailsVC.patientPayoutDetails = self.selectedPatientPayment
patientPaymentDetailsVC.currentRemittance = self.currentRemittance
patientPaymentDetailsVC.doctorNumber = self.doctorNumber
}
}
}
}
func getPatientList() {
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.show(withStatus: "Retrieving Patient List")
APIService.PatientList.getPatientList(doctorNumber: doctorNumber, periodId: currentRemittance.periodId) { (patientListArray, error) in
guard let patientListPerPayout = patientListArray, error == nil else {
if let networkError = error {
switch networkError {
case .noRecordFound:
let alertController = UIAlertController(title: "No Record Found", message: "You don't have current payment remittance", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
case .noNetwork:
let alertController = UIAlertController(title: "No Network", message: "\(networkError.rawValue)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
default:
let alertController = UIAlertController(title: "Error", message: "There is something went wrong. Please try again", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
SVProgressHUD.dismiss()
return
}
self.patientList = patientListPerPayout
self.latestCreditedAmountTableView.reloadData()
SVProgressHUD.dismiss()
return
}
}
**getPerPatientPAyoutDetails(from: String) function**
func getPerPatientPayoutDetails(from: String) {
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.showInfo(withStatus: "Retrieving Patient Details")
APIService.PatientList.getPatientDetailsPerPayout(periodId: currentRemittance.periodId, doctorNumber: doctorNumber, parameterName: .selectedByHospitalNumber, hospitalNumber: from) { (patientPayout, error) in
guard let patientPerPayoutDetails = patientPayout, error == nil else {
if let networkError = error {
switch networkError {
case .noRecordFound:
let alertController = UIAlertController(title: "No Record Found", message: "You don't have current payment remittance", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
case .noNetwork:
let alertController = UIAlertController(title: "No Network", message: "\(networkError.rawValue)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
default:
let alertController = UIAlertController(title: "Error", message: "There is something went wrong. Please try again", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
SVProgressHUD.dismiss()
return
}
self.selectedPatientPayment = patientPerPayoutDetails
print(self.selectedPatientPayment)
SVProgressHUD.dismiss()
return
}
}
Base on the gePatientList() function, it will just pull the full list of the patients. I don't know how I will pass the data of the selected patient to another VC. Hope you can help me. Thank you so much.
Codes that might help to understand the flow of my codes
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 0: break
case 1: let selectedpatient = patientList[indexPath.row].hospitalNumber
print(selectedpatient!)
self.isBrowseAll = false
getPerPatientPayoutDetails(from: selectedpatient!)
default: break
}
}
Below is the another View Controller that will display the patientName and hospitalNumber
PatientPaymentDetailsVC
class PatientPaymentDetailsViewController: UIViewController {
#IBOutlet weak var patientProcedureTableView: UITableView!
#IBOutlet weak var hospitalNumberLabel: UILabel!
#IBOutlet weak var patientNameLabel: UILabel!
var currentRemittance: CurrentRemittance!
var doctorNumber: String!
var isBrowseAll: Bool!
var patientList: [Patient]!
var patientPayoutDetails: [PatientPayoutDetails]!
override func viewDidLoad() {
super.viewDidLoad()
setupPatientInfo()
}
//MARK: FUNCTION
func setupPatientInfo() {
self.patientNameLabel.text = patient.patientName
self.hospitalNumberLabel.text = patient.hospitalNumber
}
The pulled data under the getPerPatientPayoutDetails function from the didselect will be displayed in PatientPaymentDetailsVC. Below is the output, as you can I see I can pull the data under getPerPatientPayoutDetails but the patientName and hospitalNumber does not display the data.
First of all don't get the data from the table view cell, get it from the data source
Connect the segue to the cell.
Delete the entire method didSelectRowAt
When prepare(for segue is called the sender parameter is the cell.
Get the index path from the cell and get the patient at that index path.
Rather than passing multiple parameters declare a var patient : Patient! property in the destination controller and hand over the patient instance.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "showPatientPaymentDetailsVC",
let cell = sender as? UITableViewCell,
let indexPath = tableView.indexPath(for: cell) else { return }
let patient = patientList[indexPath.row]
getPerPatientPayoutDetails(from: patient.hospitalNumber)
let patientPaymentDetailsVC = segue.destination as! PatientPaymentDetailsViewController
patientPaymentDetailsVC.patient = patient
patientPaymentDetailsVC.patientPayoutDetails = self.selectedPatientPayment
patientPaymentDetailsVC.currentRemittance = self.currentRemittance
patientPaymentDetailsVC.doctorNumber = self.doctorNumber
}
class PatientPaymentDetailsViewController: UIViewController {
#IBOutlet weak var patientProcedureTableView: UITableView!
#IBOutlet weak var hospitalNumberLabel: UILabel!
#IBOutlet weak var patientNameLabel: UILabel!
var currentRemittance: CurrentRemittance!
var doctorNumber = ""
var isBrowseAll = false
var patient : Patient!
var patientPayoutDetails: [PatientPayoutDetails]!
override func viewDidLoad() {
super.viewDidLoad()
setupPatientInfo()
}
//MARK: FUNCTION
func setupPatientInfo() {
self.patientNameLabel.text = patient.patientName
self.hospitalNumberLabel.text = patient.hospitalNumber
}
Side note:
Don't declare patientList as implicit unwrapped optional, declare it as non-optional empty array
var patientList : [Patient]()
Use tableView(_:didSelectRowAt:) method by conforming to UITableViewDelegate.
Get the selected patient as displayed below:
selectedPatient = tableView[indexpath.row] as! [String:Any]
As per your edited question, try this:
let patient = patientList[indexPath.row] as! Patient
I am having trouble passing back data that is scanned by my custom barcode scanner.
The data is read successfully and I am able to assign the value to a variable. But I cannot pass the data back to the previous view controller to populate a text view.
I am using this below to pass to my barcode VC to store the data inside it
var barcodeScanData: String = ""
I am using prepare for segue below to
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "BarcodeScanVC" {
let desitnationVC = segue.destination as! BarcodeScanVC
desitnationVC.xyz = barcodeScanData
}
}
Below here is where I am attempting to send back the data from my custom barcode scanner
var xyz: String = ""
func launchApp(barcodeScan: String) {
if presentedViewController != nil {
return
}
let alertPrompt = UIAlertController(title: "Barcode Found", message: "\(barcodeScan)", preferredStyle: .actionSheet)
let confirmAction = UIAlertAction(title: "Confirm", style: UIAlertAction.Style.default, handler: { (action) -> Void in
let barcodeData = PartsVCDetail()
self.xyz = barcodeScan
barcodeData.barcodeScanData = self.xyz
print(self.xyz, "This is what I am sending")
print(barcodeData.barcodeScanData, "This is what I am sending it TO" )
self.navigationController?.popViewController(animated: true)
})
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: nil)
alertPrompt.addAction(confirmAction)
alertPrompt.addAction(cancelAction)
present(alertPrompt, animated: true, completion: nil)
}
The two print lines
print(self.waybill, "This is what I am sending")
print(barcodeData.barcodeScanData, "This is what I am sending it TO"
Show me the correct scan data, however, when I use the last line below:
self.navigationController?.popViewController(animated: true)
The data is lost and I see an empty value in my viewDidAppear on the first view controller:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
print(barcodeScanData, "This is empty but it shouldnt be")
dataFromBarcodeScanner.text = barcodeScanData
}
What am I missing ?
With this code let barcodeData = PartsVCDetail() you are creating a new instance of PartsVCDetail and then setting the property of that instance. As soon as the action ends this instance will be deallocated and you will return to the previous view controller via popViewController.
A common solution to your requirement is a delegation pattern.
You declare a protocol for your delegate to implement
You have the original view controller implement this delegate protocol
You have the original view controller set itself as the second view controller's delegate
The second view controller can invoke the delegate method to pass the data back
Protocol
protocol BarcodeScanDelegate {
func didScan(barcodeData: String)
}
PartsVCDetail
class PartsVCDetail: UIViewController, BarcodeScanDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let desitnationVC = segue.destination as? BarcodeScanVC {
desitnationVC.delegate = self
}
}
func didScan(barcodeData: String) {
self.barcodeScanData = barcodeData
}
}
BarcodeScanVC
var delegate: BarcodeScanDelegate?
func launchApp(barcodeScan: String) {
guard presentedViewController == nil else {
return
}
let alertPrompt = UIAlertController(title: "Barcode Found", message: "\(barcodeScan)", preferredStyle: .actionSheet)
let confirmAction = UIAlertAction(title: "Confirm", style: UIAlertAction.Style.default, handler: { (action) -> Void in
self.delegate?.didScan(barcodeData: self.xyz)
self.navigationController?.popViewController(animated: true)
})
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: nil)
alertPrompt.addAction(confirmAction)
alertPrompt.addAction(cancelAction)
present(alertPrompt, animated: true, completion: nil)
}
I'm using UserDefaults to store and retrieve my data but I have no idea why when I perform an unwind segue, my userdefaults data could not be passed over to the next view controller but if i do a normal push segue, my userdefaults data could be seen in the next viewcontroller.
P.S: ONLY self.performSegue(withIdentifier: "unwindSegue", sender: nil) is the unwindSegue.
FirstViewController
#IBAction func Button1(_ sender: Any) {
var i: Int = 0
let randomNumber = arc4random_uniform(3) + 1
i = Int(randomNumber)
let alert = AlertController(title: "Congratualations", message: "You earned \(i) tokens!", preferredStyle: .alert)
alert.setTitleImage(UIImage(named: "token"))
// Add actions
alert.addAction(UIAlertAction(title: "Let's See!", style: UIAlertActionStyle.default, handler: { (action) in
var returnValue: Int = UserDefaults.standard.integer(forKey: "tokens")
returnValue = returnValue + i;
UserDefaults.standard.set(returnValue, forKey:"tokens")
self.performSegue(withIdentifier: "unwindSegue", sender: nil)
}))
if (returnValue != 30){
alert.addAction(UIAlertAction(title: "Continue Playing", style: UIAlertActionStyle.default, handler: { (action) in
var returnValue: Int = UserDefaults.standard.integer(forKey: "tokens")
returnValue = returnValue + i;
UserDefaults.standard.set(returnValue, forKey:"tokens")
self.performSegue(withIdentifier: "continue", sender: self)
}))
}
self.present(alert, animated: true, completion: nil)
}
SecondViewController
import UIKit
class TokenController: UIViewController {
#IBOutlet var tokens: UILabel!
#IBOutlet var minus1: UIButton!
#IBOutlet var minus2: UIButton!
var returnValue: Int = UserDefaults.standard.integer(forKey: "tokens")
override func viewDidLoad() {
super.viewDidLoad()
tokens.text = "x\(returnValue)"
}
#IBAction func unwindSegue(_ sender: UIStoryboardSegue) {
}
Because when you do a Push of a new ViewController, this is loaded into memory and it's method "ViewDidLoad" is called.
When you do an unwind segue , the previous ViewController is already in memory, so the viewDidLoad and all the initialization are not called and the labels and all the graphics are not updated.
This is not a problem, because you can access all the data you needed in the unwindSegue implementation through the variable sender. See this example:
func unwindToViewController(segue: UIStoryboardSegue) {
let vc_source = segue.source as! SecondViewController
self.myLabel.text! = vc_sorgente.myTextField.text!
}
#Kinja Please refer this url to use unwind segue
Click here:- Perform action when segue is closed