Change the text of the button from ViewController which presented modally - ios

There is just a button rimB, in GeneralChooser controller when i clicking on it, i call ChooserPopupViewController which presented modally, where info is returned back when i click on a row , to the previous GeneralChooser controller and displayed in ViewDidLoad ()
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "GeneralChooser") as! GeneralChooser
controller.idSet = idAr[indexPath.row]
controller.nameSet = nameAr[indexPath.row]
dismiss(animated: true)
present(controller, animated: false, completion: nil)
in ViewDidLoad () in GeneralChooser controller i see my passed info
print("id:", idSet)
print("name:", nameSet)
BUT, i cannot set this string on my button`s text!
rimB.setTitle(nameSet, for: .normal)
What is the problem? Perhaps i need to redraw my controller?

The major problem I can see from your code is that, you presented ChooserPopupViewController on GeneralChooser instance 1. Then when selecting a cell on ChooserPopupViewController, you are creating another instance 2 of GeneralChooser, and present it on ChooserPopupViewController while it is being dismissed. So I am pretty sure you are seeing the original GeneralChooser instance 1 after you dismiss the ChooserPopupViewController, but you passed the idSet and nameSet to the dismissed GeneralChooser instance 2. That is not how you should be passing info back from a presented view controller. Check this tutorial for different ways you can do it (specially sections with properties, delegation or closures)
https://learnappmaking.com/pass-data-view-controllers-swift-how-to/

You need to use a delegate to pass back the information. what you're actually doing is creating an entirely new instantiation of GeneralChooser and setting the variables there. Which is why you're not seeing the changes being made on the original GeneralChooser.
What you should be doing is:
1) creating a protocol to act as a delegate
2) Add a delegate method to the protocol that GeneralChooser would need to implement
3) Add a delegate variable within ChooserPopupViewcontroller and have GeneralChooser set it to "self" prior to presenting "ChooserPopupViewController.
4) Have ChooserPopupViewController call the protocol method from the delegate variable, in order to pass back the information to GeneralChooser. Then update the GeneralChooser button title, prior to dimissing the ChooserPopupViewController.
Hope this helps!

Related

"Attempt to present VC on VC whose view is not in the window hierarchy!" when button pressed from tableview

Trying to build my first app. Using a TableView to present 4 different sections. Each section has its own type of cell, which is defined in a .xib file. In the last section of the TableView, a cell is presented with two buttons, plus and minus.
When, for example, the plusbutton is pressed, it calls the following code in the class related to the cell:
// link to the viewcontroller with the tableview in it
let mainViewController = ViewController()
// func called when plusbutton is tapped
#IBAction func plusButtonTapped(_ sender: UIButton) {
// viewcontroller that should popup when the button is tapped
let popup = PopupViewController()
let sbPopup = SBCardPopupViewController(contentViewController: popup)
// call to show()
sbPopup.show(onViewController: mainViewController)
}
// show() function that is called
public func show(onViewController viewController: UIViewController) {
self.modalPresentationStyle = .overCurrentContext
viewController.present(self, animated: false, completion: nil)
}
When I tap the button in the simulator, it should present the PopupViewController, but the following warning shows:
Warning: Attempt to present "balance1.SBCardPopupViewController: 0x7fdf8752cb20> on <balance1.ViewController: 0x7fdf8743cac0> whose view is not in the window hierarchy!
I tried researching the warning and found some other topics relating to it. None of the solutions worked for me. Maybe it has to do with the fact that I'm calling the plusButtonTapped() from a cell defined in a .xib file?
I followed this tutorial on youtube: https://www.youtube.com/watch?v=9yUIOjykPDU
All went wel untill 10:36 where the show() is called on the same VC where the button lives that calls the function. In my case the plusButton that calls the show() function lives on a cell that is defined in a separate .xib file and thus not on the (main) VC (as shown in the tutorial). Does this have anything to do with the problem I am facing?
The error message looks like it's right. Just looking at your code the "dangling" mainViewController hasn't been added to a window yet.
// link to the viewcontroller with the tableview in it
let mainViewController = ViewController()
This comment above this line seems to indicate that it's a link to the parent viewcontroller, but it's actually a freshly instantiated viewcontroller. You'd need to pass the actual parent down through the hierarchy to achieve what you're trying.
You could make it easier on yourself if you gave each cell a "presentationDelegate" of some sort, that would handle all presentation of cards.

Swift-4: Can't move to another ViewController from a ViewController

I have designed a UITableviewCell using Xib. The cell has a button when the button clicked it should move to another viewController. I have written delegate method to trigger the function in viewController, in the function, I used PushViewController but It has not moved to another ViewController so how to resolve it?. Also, I have included UINavigationController as RootViewController by Embaded in the project. The following method is triggered by the button when clicked.
func showMoreAboutLeads(index: IndexPath)
{
print("Show_More_About_Leads:",index)
let detailController = self.storyboard?.instantiateViewController(withIdentifier: "DetailsLeadController") as! DetailsLeadController
detailController.leadsDetail = self.leadsArray[index.row] as! NSDictionary
self.navigationController?.pushViewController(detailController, animated: true)
}
if the function is called when clicked, you must make sure that the page has a navigation bar or not.
otherwise you will not be able to push the page but must present the page
self.present(detailController, animated: true, completion: nil)

Using viewDidAppear to present a View Controller, re-opening it when it's closed

In my App, I've created a new storyboard that serves as a very basic tutorial for how to use certain features. (Instructions.storyboard). This storyboard has it's own class - InstructionsVC.swift
I want to present InstructionsVC when MainVC loads within viewDidAppear.
It works great. Fires up on App load just like it's supposed to. The problem occurs when I press the [Close] button on the Instructions interface. It closes the VC, fades to the main screen, and then immediately fires the Instructions VC back up.
How can I prevent the Instructions VC from loading back up once it's closed?
func openInstructions() {
let storyboard = UIStoryboard(name: "Instructions", bundle: nil)
let instructionsView = storyboard.instantiateViewController(withIdentifier: "instructionsStoryboardID")
instructionsView.modalPresentationStyle = .fullScreen
instructionsView.modalTransitionStyle = .crossDissolve
self.present(instructionsView, animated: true, completion:nil)
}
override func viewDidAppear(_ animated: Bool) {
openInstructions()
}
And within my instructions class, I have the following action on the close button:
#IBAction func closeButtonPressed(_ sender: UIButton) {
let presentingViewController: UIViewController! = self.presentingViewController
presentingViewController.dismiss(animated: true, completion: nil)
}
Note - I'd rather not use UserDefaults to resolve this, because I'm going to be incorporating something similar in other parts of the App and don't want to resort to UserDefaults to achieve the desirable behavior.
Thanks in advance buddies!
viewWillAppear and viewDidAppear are called every time a view controller's content view becomes visible. That includes the first time it's rendered and when it's shown again after being covered by a modal or by another view controller being pushed on top of it in a navigation stack.
viewDidLoad is only called once when a view controller's content view has been loaded, but before it is displayed. Thus when viewDidLoad is called it may be too soon to invoke your second view controller.
You might want to add an instance variable hasBeenDisplayed to your view controller. In viewDidAppear, check hasBeenDisplayed. If it's false, display your second view controller and set hasBeenDisplayed to true.

Performing Segue from - TableViewCell.xib to UIView - and - TableView.xib to UIViewController -

How can I perform segue from one .xib (TableCell.xib) to another .xib(UIViewController) on click on TableCell?
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(results?[indexPath.row]) // prints successfully
...
// apply below chunks here...
}
First, I tried setting up a manual segue in Storyboard. Note that, TableView & TableViewCell are external xib but parent of TableView is in Storyboard. So, I tried control-dragging from parent-ViewController to the detailViewController (and identifier: showDetails)..However, doesn't work.
...
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.performSegueWithIdentifier("showDetails", sender: AnyObject?())
})
...
Obviously, I received this error (because TableView is in .xib whereas parent and detailsVC are in Storyboard and I ctrl+dragged from parentVC to detailsVC but TableView1 apparently doesn't recognise the segue in Main Storyboard):
TableView1: 0x7f97626a3280>) has no segue with identifier 'showDetails''
Then I tried creating a ViewController and tried adding var parentNavigationController : UINavigationController! at the top and referencing below chunk inside didSelectRowAtIndex
...
let newVC : UIViewController = CellDetailsVC()
newVC.title = "DetailsView"
print(newVC)
parentNavigationController!.pushViewController(newVC, animated: true)
However, I am receiving an error:
DetailsVC: 0x7ff7794ae550>
fatal error: unexpectedly found nil while unwrapping an Optional value
Finally, I also tried below chunk inside didSelectRowAtIndex, and got something close (as I can use dismissView to < Back); but not exactly what I want. Its problem is that the segue animation looks like the page is coming from bottom.
...
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("VideoDetailsVC") as UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
I am also receiving a warning (but not crash) for some reason I couldn't figure out:
Presenting view controllers on detached view controllers is discouraged
Which approach is the right one? Or is there a better way to adapt to achieve what I am trying to?
Firstly, what is the right way to perform segue from didSelectRowAtIndex (in TableView.xib class) to a new ViewController (either Storyboard or xib)?
Secondly, assume that there is an image thumbnail in the TableViewCell.xib. What is right way of performing segue to a new UIView on click on that view? (like full-screen image / dismiss-able)
Your second approach looks to be right one but of course you are doing something wrong there. You need to drag and drop Segue from Parent ViewController to Detail ViewController and give it a Segue identifier in Storyboard (check attached Image below). IN didSelectRowAtIndexPath you need to do below code:
self.performSegueWithIdentifier("showDetailViewController", sender: nil)
As I can see you already tried that but it looks like you missed some step either setting Segue Identifier or giving wrong identifier in performSegueWithIdentifier method. Also your Parent ViewController should be embedded in UINavigationController in case you are missing that thing :)
Also keep in mind you should perform navigation (here its Segue) from one ViewController to another ViewController at same level not from one View to another ViewController.

Segue VS didDeselectRowAtIndexPath

I am using didDeselectRowAtIndexPath to navigate through different Storyboards like:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let story = ["News","Video","Twitter","Request Info","More"]
let vc = self.storyboard?.instantiateViewControllerWithIdentifier(story[indexPath.row]) as NewsFeedTableViewController
self.presentViewController(vc, animated: true, completion: nil)
}
However, the Navigation Bar does not show up when I use this function. But, when I use segue, the Navigation Bar shows up.
Now the Problem is:
Cells in a tableView cannot segue more than 1 storyboard
Need Navigation Bar to Scroll in the App (which seems to need Segue)
Any solutions through this?
You are using presentViewController, that's why you are not getting the NavigationBar, it presents the view modally. Instead of that pushViewController like:
self.navigationController?.pushViewController(vc, animated: true)
Probably in your storyboard the segue type will be push, that's why you are getting the Navigation Bar.
You might be interested in the following:
presentViewController:animated:completion:
ViewController Programming Guide for iOS
You are presenting the next view controller, not pushing it. (at least in case 1) So you have a modal link and no nav controller !
You can either use .pushViewController or simply performSegue and make sure your segue type is push. I would go for the performSegue and change the identifier name in each switchcase.
And you can still pass data in the prepareForSegue
Better to call performSegue in didSelectRow. And perform actions in perpareSegue Method .

Resources