ViewDidAppear not called automatically - swift

I have a HomeViewController that is opening a WelcomeViewController using segue and is from type Sheet.
In WelcomeViewController the User is entering information and then closing(dismissing) it. After WelcomeViewController is closed I want to update HomeViewController by changing the String value of a TextField.
The Problem is, that ViewDidAppear in HomeViewController is not called after dismissing WelcomeViewController. I did not have this Problem before when developing for iOS.
How can I fix this? I tried to call it manually but got problems, because my IBOutlets were set to nil and I could not change the text in my TextField.
This is the code that invokes the segue:
self.performSegue(withIdentifier: "welcome", sender: self)
this is my viewDidAppear in HomeViewController:
override func viewDidAppear() {
super.viewDidAppear()
if (CoreDataAccess().countUsers() == 0) {
self.performSegue(withIdentifier: "welcome", sender: self)
} else {
//continue execution..
}
}
In WelcomeViewController I dismiss the ViewController using this code
dismiss(self)

This is how I solved it using a delegate.
Thanks to #ullstrm for the suggestion.
First I added this protocol to HomeViewController
protocol WelcomeDelegate {
func insertData(_ name: String, birthday: Date)
}
Then this prepare(for segue:)
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destinationController as? WelcomeViewController {
destinationViewController.delegate = self
}
}
And this extension at the bottom (I know it's not ok to call viewDidAppear manually , but it's working for now)
extension HomeViewController : WelcomeDelegate {
func insertData(_ name: String, birthday: Date) {
viewDidAppear()
}
}
Then in WelcomeViewController I added this variable
var delegate:WelcomeDelegate? = nil
And this line right before dismiss(self)
self.delegate?.insertData(tfName.stringValue, birthday: dpAge.dateValue)

Add your code to ViewWillAppear() instead of ViewDidAppear().
or you call a method in the HomeViewController when dismissing WelcomeViewcontroller
dismiss(animated: true) {
self.presentedViewController.myMethod()
}

Related

Change variable while dismissing modal controller

EDIT: I have decided to change the way my app works, so this problem is solved. Thanks to everyone who helped!
I have a modal controller where when I press a button it dismisses the view. What I want to do is change a variable in another view controller when I dismiss it, is that possible? Or, if this doesn't work, is there a way for me to access the changed variable of another swift file? I will add my code below:
class PopupViewController: UIViewController {
var event = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func dismiss(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func event910(_ sender: Any) {
event = "storyTime"
dismiss(animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ViewController
vc.event = event
}
}
I want to pass the changed variable "event" to another view controller, how can I do this?
Delegate View Controller is as follows. : -
it is the place where you will send the data to the next swift file
protocol myprotocol {
func anyfunction(_ param1:String)
}
struct mystruct1 {
var delegate:myprotocol?
// where you want tot start the delegate / send the data to the next file
func anymethod(){
delegate.anyfunction(sendTheDataYouWant)
}
}
// it is here you will receive the data
class anyclass:UIViewController ,myprotocol {
let class1 = mystruct1()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
class1.delegate = self
}
func anyfunction(param1:String){
// here Save the data you want
// because this function will be triggered as delegate will be called
}
}
ps:- I reccomend you to read https://docs.swift.org/swift-book/LanguageGuide/Protocols.html
& apple docs

How to reload data in UITableView when popping view controller

If I have two ViewControllers one which contains a UITableView and another which updates data in the tableView. How would I reload the table data once I pop of the viewController and go back to the view with the tableView?
I already tried using viewDidAppear
You could use viewWillAppear just like Rajesh suggested:
override func viewWillAppear(_ animated: Bool) {
tableView.reloadData()
}
Or you could use a callback function to pass data and reload view controller 1's tableview.
In ViewController 2, define your callback function:
// Callback function
var callbackResult: ((data) -> ())?
And call it before going back to ViewController 1:
callbackResult?(data)
self.navigationController?.popViewController(animated: true)
In ViewController 1, use the callback function's closure to collect the result and reload your tableView. This can happen inside prepareForSegue, for example:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToViewController2" {
let destinationVC = segue.destination as! ViewController2
// Set any variable in ViewController2
destinationVC.callbackResult = { result in
// assign passing data etc..
self.tableView.reloadData()
}
}
}
You may try doing something like this:
class TableViewController: UITableViewController {
func showUpdatingViewController() {
let vc = UpdatingViewController()
vc.onUpdate = { [weak self] in
self?.tableView.reloadData()
}
navigationController?.pushViewController(vc, animated: true)
}
}
class UpdatingViewController: UIViewController {
var onUpdate: (() -> Void)?
func updatesFinished() {
onUpdate?()
dismiss(animated: true, completion: nil)
}
}
I was having similar issue and using viewWillAppear or viewDidAppear did not help me to reload the tableview.
I solved my situation by putting the reloadData() call within the unwindSegue
#IBAction func unwindToVCSetupDataScreen(_ unwindSegue: UIStoryboardSegue) {
/// Nothing is actually needed here
/// https://www.youtube.com/watch?v=WaSlHXNah7E #6:25
/// CTRL-Drag from Back button to the "exit" square at the very top of VC
tableView.reloadData()
}

Value not passing between viewControllers

Struggling to get my viewControllers to send value from the main viewController to a second. I want it to happen on a button click, I'm going to get the value from the button and pass it to the new form. But it just isn't working.
Code for main ViewController
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func butClick(_ sender: UIButton) {
NSLog("Button Pressed : %#",[sender .currentTitle])
//var tt = [sender .currentTitle]
// Create the view controller
let vc = TimesTablesViewController(nibName: "TimesTablesViewController", bundle: nil)
vc.passedValue = "xx"
self.performSegue(withIdentifier: "pushSegue", sender: nil)
}
}
Code for second viewController called TimesTablesViewController:
class TimesTablesViewController: UIViewController {
#IBOutlet weak var titleLabel: UILabel!
var passedValue:String = ""
override func viewDidLoad() {
super.viewDidLoad()
titleLabel?.text = "\(passedValue) Times Table"
}
}
I've followed tutorials but can't seem to solve the problem! Thanks for any help!
Replace
self.performSegue(withIdentifier: "pushSegue", sender: nil)
with
self.present(vc,animated:true,completion:nil)
or ( if the current vc is inside a naigation )
self.navigationController?.pushViewController(vc,animated:true)
Using
self.performSegue(withIdentifier: "pushSegue", sender: nil)
is fit with storyboards not xibs and if this your case then you need to use the above line only inside the button action with implementing this method inside the source vc
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pushSegue" {
if let nextViewController = segue.destination as? TimesTablesViewController{
nextViewController.passedValue = "xx"
}
}
}
I’m assuming that the new view controller is appearing, but you’re simply not seeing the data. If so, you’re evidently using storyboards. The TimesTablesViewController(nibName:bundle:) only works if you’re using XIB/NIBs and manually presenting new view controller.
If you’re really using storyboards, simplify your butClick method:
#IBAction func butClick(_ sender: UIButton) {
NSLog("Button Pressed")
performSegue(withIdentifier: "pushSegue", sender: self)
}
But implement prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? TimesTablesViewController {
destination.passedValue = "xx"
}
}
Assuming the above fixes your problem, I might suggest a further simplification. Notably, if your butClick(_:) method is really only calling performSegue, you can segue to this next scene without any #IBAction method at all:
remove butClick(_:) entirely;
remove the connection between the button and the butClick method in IB, on the “Connections Inspector” tab in the right panel; and
control-drag from the button previously hooked up to butClick(_:) to the scene for TimesTablesViewController.
That will simplify your code further.

nothing happens on trying to dismiss popover viewcontroller Swift

I have a viewcontroller that is presented as popover when the user clicks on an ImageView.
The problem is, I added a button to dismiss it but when I tap on it nothing happens.
The code I have is:
#IBAction func onCloseTapped(_ sender: Any) {
presentedViewController?.dismiss(animated: true, completion: nil)
}
I've also tried dismiss(animated: true, completion: nil) and other methods, but still nothing.
Can anyone tell me what I am doing wrong?
Edit: Posting a screenshot:
Edit 2: I'm presenting it from the storyboard. I've added a gesture recognizer on the image, then added segue from the storyboard that says present as popover, then anchor to the image.
it won't work because when you show as popover, the viewController doesn't have the navigationController. You have to create a delegate method and use the dismiss function on the viewController that make the call to the popover.
Here an exemple:
make the popover delegate, in the popover viewController:
protocol PopoverViewControllerDelegate: NSObjectProtocol {
func dismiss()
}
then you create a delegate variable and call when the button is tapped:
var delegate: PopoverViewControllerDelegate?
#IBAction func onCloseTapped(_ sender: Any) {
delegate?.dismiss()
}
Now in the viewController that call the popover you override the prepare for segue method to set the popover delegate:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popover" {
if let vc = segue.destination as? PopoverViewController {
vc.delegate = self
}
}
}
Now you just need to use the delegate to dismiss your popover viewController:
extension ViewController: PopoverViewControllerDelegate {
func dismiss() {
navigationController?.dismiss(animated: true, completion: nil)
}
}
Don't forget to put the identifier for you segue, the identifier that we use is this = "popover"
Hope that help you.

Disable textfield in destination view controller

I have two view controllers: ViewController1 and ViewController2.
My objective is, when the segue is triggered if a certain condition in ViewController1 is met a textfield in viewController2 to be disabled.
I have setup shouldPerformSegueWithIdentifier and prepareForSegue and everything works fine, but when i put the condition it crash saying that it found an error unwrapping an optional- the textfield.
my ViewController1 is :
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject!) -> Bool {
if condition1=true{
return true
}
else{
return false
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier=="segue"){
let destVC:ViewController=segue.destinationViewController as! ViewController2
if n==1{
destVC.myTextField.enabled=false
}
}
}
In prepareForSegue method myTextField of ViewController2 is not initialized yet, so thats why you are getting an error unwrapping an optional textField, To solve your crash create one Bool instance inside your ViewController2 and pass its value in prepareForSegue method.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier=="segue"){
let destVC:ViewController=segue.destinationViewController as! ViewController2
if n==1{
destVC.isEnabled=false
}
}
}
Create instance isEnabled inside ViewController2 like this and used it in the viewDidLoad of ViewController2
var isEnabled: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
self.myTextField.enabled = self.isEnabled
}
The views associated with the destination controller are not yet loaded/instantiated at that point.
Create an instance boolean flag on the destination VC and set that in the prepare segue function. Then in viewDidLoad or later in the destination VC check for that switch and disable the text field.
You can set condition at your destination view controller then you can do this
myTextField.userInteractionEnabled = false
Enable and disable userInteractionEnabled of your textfield.