i am new to iOS here is my question:
I have a saveCardViewController (Presented Modally) with some textFields and Save button.
#IBAction func Save(_ sender: UIButton) {
date = datePicker.date
try! realm.write() {
sessionCard.pokerType = pokerTypeSegment.titleForSegment(at: pokerTypeSegment.selectedSegmentIndex)!
date = dateFormatter.string(from: date)
sessionCard.handsPlayed = Int(handPlayedTextlabel.text!) ?? 0
sessionCard.moneyIn = Int(moneyInTextLabel.text!) ?? 0
sessionCard.moneyOut = Int(moneyOutTextLabel.text!) ?? 0
sessionCard.timePlayed = Int(timePlayedTextLabel.text!) ?? 0
sessionCard.sortDate = date
realm.add(sessionCard)
}
dismiss(animated: true, completion: nil)
}
How can I reloadData() on my main ViewController, after Save button is pressed and saveCardViewController is dismissed.
Thanks!
EDIT # 1:
Thank you #davidev for your answer,I made changes but still does not update
My ViewController With TableView:
class SessionViewController: BackgroundViewController, RefreshViewDelegate {
func refreshView() {
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
My ViewController with data and Save button:
protocol RefreshViewDelegate {
func refreshView()
}
class AddSessionViewController: UIViewController, UITextFieldDelegate {
var delegate: RefreshViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func TEST2(_ sender: UIButton) {
delegate?.refreshView()
dismiss(animated: true, completion: nil)
}
You can use delegate pattern to achieve this.
Declare your Refresh Protocol like this:
protocol RefreshViewDelegate {
func refreshView()
}
Make your parent view conform to this protocol and implement refreshView() with your custom refresh action. Also make sure to set the delegate of the child view to self.
Inside saveCardViewController declare your delegate variable
var delegate : RefreshViewDelegate?
And call the delegate action inside your IBaction
delegate?.refreshView()
Edit:
I just saw your updated code. As you are using Storyboard segues, you still have to set the delegate via code. In your main view controller add the function:
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
if let viewController = segue.destinationViewController as? AddSessionViewController
{
viewController.delegate = self
}
}
Related
I have a NewCartViewController that's embedded in a UINavigationController
In my ContainerViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToCart" {
self.cartVC = segue.destination as? NewCartViewController
self.cartVC?.delegate = self
print("cartVC Delegate", cartVC?.delegate) // value is NOT nil here
let pendingCart = PendingCart(color: .blue)
self.cartVC?.setupPaintOrder(cart: pendingCart)
}
}
In my NewCartViewController
protocol NewCartViewControllerDelegate: class {
func vcDidFinishWithCancel(_ newCartViewController: NewCartViewController)
}
class NewCartViewController: UIViewController {
weak var delegate: NewCartViewControllerDelegate?
....
#objc func dismissVC() { // called from the leftBarButtonItem
print(delegate) // nil
}
I've looked at other answers that are related but I'm not sure what's going on here. Any help would be appreciated. Thanks!
Edit - more detail on how the VC is instantiated:
My BuyButtonVC calls a delegate method back to AddToCartContainerVC:
extension AddToCartContainerViewController: BuyButtonViewControllerDelegate {
func buyButtonVCDidPressButton(_ vc: BuyButtonViewController) {
performSegue(withIdentifier: "goToCart", sender: self)
}
}
Which then fires the prepare(for segue:) function from the first code block above where the we look at the the segue.destination and set the delegate.
I have a print on the didSet on the delegate in my NewCartViewController and I see that the delegate is set.
The user goes to the screen, the didSet is never called again as if it was getting set to nil. But then when I try to call the delegate method in the second code block, the delegate is nil
I have tried with the following code and seems to work fine.
Few times we get this unknown behavior and is frustrating when stuck. Try to clean build and quit Xcode clear derived data and do it again.
class ContainerViewController: UIViewController, NewViewControllerDelegate {
weak var cartVC: NewViewController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToCart" {
self.cartVC = segue.destination as? NewViewController
self.cartVC?.delegate = self
print("cartVC Delegate", cartVC?.delegate) // value is NOT nil here
// let pendingCart = PendingCart(color: .blue)
// self.cartVC?.setupPaintOrder(cart: pendingCart)
}
}
func vcDidFinishWithCancel(_ newViewController: NewViewController) {
print(newViewController.delegate)
self.navigationController?.popViewController(animated: true)
}
}
protocol NewViewControllerDelegate: class {
func vcDidFinishWithCancel(_ newViewController: NewViewController)
}
class NewViewController: UIViewController {
weak var delegate: NewViewControllerDelegate?
#IBOutlet weak var leftBarButtonItem: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(dismissVC))
}
#objc func dismissVC() { // called from the leftBarButtonItem
print(delegate) // nil
delegate?.vcDidFinishWithCancel(self)
}
}
Output: when leftbarbuttonitem clicked
cartVC Delegate Optional(<NetworkExtensionTest.ViewController: 0x7f84c6d07670>)
Optional(<NetworkExtensionTest.ViewController: 0x7f84c6d07670>)
Optional(<NetworkExtensionTest.ViewController: 0x7f84c6d07670>)
any help is appreciated.
I am new to Ios development and I am trying to change a label text which is located in my first initial view controller. I want this text to change as I press a button in the second view controller which is segued to the initial one.
here is my first view controller
import UIKit
protocol gameModeDelegate {
func didTapChoice(test:String)
}
class ViewController2: UIViewController {
var selectionDelegate:gameModeDelegate!
#IBAction func chooseButton(_ sender: Any) {
selectionDelegate.didTapChoice(test: "TEST")
let selectVC = storyboard?.instantiateViewController(withIdentifier: "VC1") as! ViewController
present(selectVC,animated: true,completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
here is what i have done in the second where the label is
override func viewDidLoad() {
super.viewDidLoad()
let selectVC2 = storyboard?.instantiateViewController(withIdentifier: "VC1") as! ViewController2
selectVC2.selectionDelegate = self
winningLabel.isHidden = true
winningLabel.center = CGPoint(x: winningLabel.center.x, y: winningLabel.center.y - 400)
playAgainoutlet.isHidden = true
playAgainoutlet.center = CGPoint(x: playAgainoutlet.center.x, y: playAgainoutlet.center.y + 400)
}
extension ViewController: gameModeDelegate{
func didTapChoice(test: String) {
CommunicationLabel.text = test
}
}
I tried these two methods so far and i keep getting this error.
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
You should not use this approach to achieve the result, you may use two different approaches to achieve the same result.
1- Use a delegate protocol approach:
in secondViewController you should declare a protocol like this
protocol applySelecction {
func applyText(text: String)
}
and in the class declare a variable like this.
var delegate: apply selection?
then in the button action
#IBAction func saveButtom(sender: UIButton){
//print(selected)
delegate?.applySelection(text: text) //text is the value select from UILAbel o the option the user select
self.dismiss(animated: true, completion: nil)
}
then in firstViewController conforms to applySelection protocol like this
class FirstViewController: UIViewController,applySelection{
func applyText(text: String){
//update the UIlabel here
2- Use a closure.
here in secondViewController you should add a new var like this,
var applyText: ((String) -> Void)?
then in
#IBAction func saveButtom(sender: UIButton){
self.applyText(text) //text is your text to update
}
and in firstViewController in prepare for segue rewrite like this.
let vc = segue.destination as! fisrtViewController)
vc.applyText = { [weak self] data in
guard let self = self else {return}
self.text = text //this is assigning the text to self-text supposing text is a UILabel in this viewController
}
You may try one of the two approaches which may seem right for you.
EDIT.
try this.
class ViewController2: UIViewController {
var selectionDelegate:gameModeDelegate!
#IBAction func chooseButton(_ sender: Any) {
selectionDelegate.didTapChoice(test: "TEST")
//if segue is a show segue
self.navigationController?.popViewController(animated: true)
//else is a modal segue.
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
PD. you dont have to present a viewcontroller already present in the view stack, only dissmis it. Good luck
I know the same question is asked many times. I read most of the answers from stack overflow and tried. But it did not help my problem.
I have two view controllers
protocol UpdateDataDelegate {
func loadData()
}
viewcontroller2 {
var delegate: UpdateDataDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
fun saveData() {
self.delegate?.loadData()
}
}
viewcontroller1 : UpdateDataDelegate {
var vc2 = viewcontroller2()
override func viewDidLoad() {
super.viewDidLoad()
vc2.delegate = self
}
func loadData() {
}
}
But function loadData() from viewcontroller1 is not called.
Since I don't have the complete code before me I can only assume that the delegate is not assumed properly.
If the delegate is not initialised properly it cannot pass value to the other viewController.
You can check delegate is properly initialised by:
if let delegate = delegate{
//Do your works here
}else{
print("The delegate is nil")
}
if the delegate is nil is printed in console, then the problem might be in the way the delegate was initialised
This might be because you are setting the delegate and opening an another instance of the viewController which was not assigned the delegate value.
In the code you provided I see that you are setting the delegate as
var vc2 = viewcontroller2()
vc2.delegate = self
But I cannot see the code that you used to move to the viewController2. Now we have to present this assigned viewController. Instead of using segue to move to the viewcontroller2 present this vc using the code below
present(vc2, animated: true, completion: nil)
You should place this according to your code logic.(where your segue is triggered)
Situation 2:
If you are using segue to move to the viewController2 then the delegate should be assigned in the prepareforSegue method as below
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc2 = segue.destination as? ViewController2{
vc2.delegate = self
}
}
let me know how it goes.
A simple playground for what you are trying to do, even if I have not clear what you are trying to achieve:
import UIKit
protocol UpdateDataDelegate: class {
func loadData()
}
class ViewController2: UIViewController {
weak var delegate: UpdateDataDelegate?
func saveData() {
self.delegate?.loadData()
}
}
class ViewController1: UIViewController {
}
extension ViewController1: UpdateDataDelegate {
func loadData() {
print("loadData called")
}
}
let viewController1 = ViewController1()
let viewController2 = ViewController2()
viewController2.delegate = viewController1
viewController2.saveData()
Few notes:
classes should be upper case. So, ViewController1 instead viewcontroller1
delegates should be weak otherwise you create reference cycles
class should be used for UpdateDataDelegate protocol otherwise compiler will complain since weak cannot be applied to class and class-bound protocol types
prefer extension to conform to protocols. It makes the code easy to read
The only thing I see missing in your code is call to saveData() of ViewController2 that will in turn call loadData() of ViewController1.
So just add:
override func viewDidLoad()
{
super.viewDidLoad()
vc2.delegate = self
vc2.saveData() //Add this line to your code
}
You are good to go now :)
Edit:
protocol UpdateDataDelegate
{
func loadData()
}
class ViewController2: UIViewController
{
var delegate: UpdateDataDelegate?
func saveData()
{
self.delegate?.loadData()
}
}
class ViewController1: UIViewController, UpdateDataDelegate
{
var vc2 = ViewController2()
override func viewDidLoad()
{
super.viewDidLoad()
vc2.delegate = self
vc2.saveData()
}
func loadData()
{
print("Done")
}
}
I have used the above code and it is working fine for me. How are you executing it? I have used storyboard and used ViewController1 as the Initial View Controller.
I assume that you need to load data when your delegate has been set up. In this case you can use magic didSet:
weak var delegate: UpdateDataDelegate? {
didSet {
self.saveData()
}
}
So right after setting the delegate the needed method will be called.
I have to view controllers, from first controller's menu item click, a segue is performed by 'Present Modally' Segue which will open the second viewController (acting as a menu) when i press Refresh button on the second VC, it is calling the method from the first VC To refresh its data and load it on tableView. the problem is it is call the method from second to first VC but is returning nil on this line.
self.tableView.dataSource = self
i think it is because the first VC is not active properly as it is in the background ( the second VC's background is transparent so that i can see the first VC at the back ). i'm new to swift and i don't know how to deal with it.
EDIT: This is the refresh method
func refresh() {
// self.dismiss(animated: true, completion: nil)
// present(DayView_Controller, animated: true, completion: nil)
// self.dismiss(animated: true, completion: nil) // it is dismissing the first view controller instead of second
////////
Extensions.ShowAlert(self.pwait, sender: self) // here it causing the exception on viewDidLoad
let defaults = UserDefaults.standard
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "MM/d/yyyy"
let finaldate = formatter.string(from:date)
if Reachability.isConnectedToNetwork(){
// all the services are being call and getting reloaded on tableview
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self // exception here
}
2nd EIDT: now i'm dismissing the 2nd VC on the refresh click and call the method from 1st VC on the completion, still getting the same error.
#IBAction func Refresh_CLick(_ sender: Any) {
self.dismiss(animated: true, completion: {
DayView_Controller().refresh()
})
// DayView_Controller().refresh()
}
I believe, you need to do something like this:
1) Create protocol:
protocol RefreshDelegate {
func dataChanged(data: [Items])
}
2) In your second view controller add:
var delegate: RefreshDelegate ?
if let del = delegate {
del.dataChanged(data: items) // your refreshed data
}
3) In your first view controller add extension:
extension FirstViewController: RefreshDelegate {
func dataChanged(data: [Items]) {
// update your tableView data -> then reload
}
}
Something like this. You should understand main idea.
Hope it helps
Use Delegate to refresh data
protocol RefreshDelegate: class {
func refreshData(data: Int)
}
In your first view controller add delegate
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, RefreshDelegate {
In your prepare for segue method set delegate
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "go" {
let viewController:SecondViewController = segue.destination as! SecondViewController
viewController.refreshDelegate = self
}
}
// Delegate
func refreshData(data: Int) {
tableData = data
self.tableView.reloadData()
}
In second view controller add variable for delegate
weak var refreshDelegate: RefreshDelegate?
Now call refresh delegate from second view controller
#IBAction func refreshTapped(_ sender: UIButton) {
self.dismiss(animated: true, completion: {
self.refreshDelegate?.refreshData(data: 10)
})
}
I've 2 ViewController's: VC1 and VC2. In VC1 i've :
#IBAction func cliclOnBtn(_ sender: UIButton) {
CameraController.takePicture()
}
...
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print("didFinishPickingMediaWithInfo", picker.sourceType)
let _image = (info[UIImagePickerControllerOriginalImage] as! UIImage)
ImageView.image = _image
Images.append(_image)
}
In VC2 i'm passing array of _image, but when i try to back on VC1, photos from camera adding by clicking again. How to reset data? How to make reset of an array of photo, when back button pressed ?
You can implement viewWillAppear in VC1, and then remove all elements of the image Array. This way, when you tap back on VC2, the VC1 will have it's image Array empty.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
Images.removeAll()
}
Btw, consider renaming your instance variables, lowercasing the initial character, it's a good practice.
Use protocol with delegate for sending data to back
You can update UI or code in delegate function
import UIKit
protocol DestinationViewControllerDelegate {
func updateData(text:String); // you can use Array, Dictonary, Modal as per your requirment
}
class DestinationViewController: UIViewController {
var delegate:FullCalendarViewDelegate! = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func doneButton(sender: AnyObject) {
delegate?.updateData("Got new data")
self.navigationController?.popViewControllerAnimated(true)
}
}
// Sender View Controller
class SenderViewController: UIViewController, DestinationViewControllerDelegate {
#IBOutlet weak var titleLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func NextButton(sender: AnyObject) {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let dash:DestinationViewControllerDelegate = storyBoard.instantiateViewControllerWithIdentifier("DestinationViewControllerDelegate") as! DestinationViewControllerDelegate
dash.delegate = self
self.navigationController?.pushViewController(dash, animated: true)
}
func updateData(text:String) {
titleLabel.text = text
}
}