I asked that question before, and I got the solution to what he sought. Now, I need to amplify my question. Using delegates, how can I create a Delegate to the ViewController send data to the ContainerView and ContainerView send data to the ViewController?
Well, I don't know if this is entirely what you're looking for, but what I've always done in this situation is kept a record of each view controller inside the other's class.
For example if your container view has the embed segue with identifier "Embed Segue" then your classes might look like:
Superview Class
Class ViewControllerOne: UIViewController {
var data = "This is my data I may want to change"
var subView: ViewControllerTwo?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Embed Segue" {
let destinationVC = segue.destinationViewController as! ViewControllerTwo
destinationVC.superView = self
self.subView = destinationVC
}
}
}
Embedded Class
Class ViewControllerTwo: UIViewController {
var data = "This is the other view controller's copy of that data"
var superView: ViewControllerOne?
}
Then you can pass data between these View Controllers simply by referencing self.subView.data and self.superView.data respectively.
Edit: For ViewControllerTwo to pass data back to ViewControllerOne, it would then simply have to reference self.superView.data. e.g:
Class ViewControllerTwo: UIViewController {
var data = "This is the other view controller's copy of that data"
var superView: ViewControllerOne?
func passDataBack() {
self.superView.data = self.data
}
}
This would then update the data variable in the first view controller.
Related
I have two view controllers (autoClaimViewController and reviewAutoViewController). In autoClaimViewController, I have a dictionary (reviewTableViewData) that is made up of a struct called claimData (it contains two string variables and a UIImage variable). When the user hits the "Review" button, I want the reviewTableViewData dictionary to be passed to the second view controller so that it's data can be displayed on a table view in the second view controller (reviewAutoViewController). How do I pass this dictionary to the other view controller?
Please make your answers understandable for a beginner - I'm still learning. I'm moving between view controllers using storyboard segues.
Thanks.
Additional Question: Will the images that I stored in the variables be passed when the dictionary is passed? In other words, do images work like Integers and Strings, where they can be passed between variables without an issue?
My code:
struct claimData {
var images = UIImage()
var imageTitle = String()
var relatedUnrelated = String()
}
class autoClaimViewController: UIViewController {
var reviewTableViewData = [claimData]()
#IBAction func reviewButton(_ sender: Any) {
//PASSES THE INFORMATION TO THE REVIEW VIEW CONTROLLER
//FIX THIS... IT NEEDS TO SEND THE reviewTableViewData ARRAY.
//MAKE A DICTIONARY IN THE REVIEW CONTROLLER THAT RECEIVES THE DICTIONARY FROM THIS VIEW CONTROLLER.
let vc2 = reviewAutoViewController()
//Find out how to transfer a dictionary from one view controller to another
}
I think your best bet is to set up a segue in your storyboards which takes you from 1st view to the second view. Here is a guide.
https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html
In both views have a place to store the information you want to pass. So create a var in the secon view to hold reviewTableViewData from the first view.
Then in the 1st view you call
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is reviewAutoViewController
{
let vc = segue.destination as? TertiaryViewController
vc?.reviewTableViewData = reviewTableViewData
}
}
This get the data ready to be sent.
Then you perform the segue and the data should be passed for you. Perfomr the seugue with this func triggered by the button or whatever you use to tranisiton between views.
func performSegue(withIdentifier identifier: String,
sender: Any?){
enter code here
}
I connected UITableViews with Show segue. I want to change some variable of destination tableView whenever I tap the Back button on navigation bar. Let's say I'm not allowed to create a new back button, how can I detect/call the destination tableView when the back button is tapped? Like in prepareforsegue we have segue.destination, is there some thing like "backbutton.destination"?
Thanks!
What you should be doing here (I think) is not trying to find the destination of the back button but passing in the interested object when going forwards.
On you detail view you could have something like...
protocol DetailViewDelegate {
func detailViewDidChange()
}
Then in the detail view controller...
class DetailViewController: UIViewController {
weak var delegate: DetailViewDelegate?
// you could call this from viewWillDisappear or something
func doThisWhenYouWantToUpdateTheTableView() {
delegate?.detailViewDidChange()
}
}
Now in the tableview...
func prepareForSegue(...) {
if let vc = segue.destination as? DetailViewController {
vc.delegate = self
}
}
and...
extension MyTableViewController: DetailViewDelegate {
func detailViewDidChange() {
// respond to the change here.
}
}
I have a view controller (containing my menu) presented on top of another view controller (my application).
I would need to access the presenting view controller (below my menu) from the presented view controller (my menu), for example to access some variables or make the presenting view controller perform one of its segues.
However, I just can't figure out how to do it.
I'm aware of the "presentingViewController" and "presentedViewController" variables but I didn't manage to use them successfully.
Any Idea ?
Code (from the presented VC, which as a reference to the AppDelegate in which the window is referenced) :
if let presentingViewController = self.appDelegate.window?.rootViewController?.presentingViewController {
presentingViewController.performSegue(withIdentifier: "nameOfMySegue", sender: self)
}
Here is a use of the delegation Design pattern to talk back to the presenting view controller.
First Declare a protocol, that list out all the variables and methods a delegate is expected to respond to.
protocol SomeProtocol {
var someVariable : String {get set}
func doSomething()
}
Next : Make your presenting view controller conform to the protocol.
Set your presenting VC as the delegate
class MainVC: UIViewController, SomeProtocol {
var someVariable: String = ""
func doSomething() {
// Implementation of do
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Your code goes here.
if let destVC = segue.destination as? SubVC{
destVC.delegate = self
}
}
}
Finally, when you are ready to call a method on the presenting VC (Delegate).
class SubVC: UIViewController {
var delegate : SomeProtocol?
func whenSomeEventHappens() {
// For eg : When a menu item is selected
// Set some Variable
delegate?.someVariable = "Some Value"
// Call a method on the deleate
delegate?.doSomething()
}
}
Assuming that VCApplication is presenting VCMenu, in VCMenu you can access VCApplication with:
weak let vcApplication = self.presentingViewController as? VCApplicationType
Your example self.appDelegate.window?.rootViewController?.presentingViewController is looking for the ViewController that presented the rootViewController - it will be nil.
EDIT
Per TheAppMentor I've added weak so there are no retain cycles.
I am creating an application in which there are 6 view controller in storyboard. The thing is that data is shared between the default view controller and the first one ( say A and B) which i added. i am using the prepareforseque method for passing data. the problem started when i added two more view controller. lets say C and D i created two new swift files and changed the two view controller class name. i created a textbox and button in C and label in D. when i pressed the button, the value of the text field is not passing into the D view controller although i used the same methods and code which i used for A and B. do i have to do anything else when i want to pass data between two newly added view controller.
first viewcontroller in which when a button is pressed value 1 needed to be passed:
class PlaySelectMenu: UIViewController {
var value = Int()
#IBAction func twotofive(sender: AnyObject) {
value = 1
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let nextView : PlayGameView = segue.destinationViewController as! PlayGameView
nextView.x = value
}
}
the second view controller which receive the value and print it
import Foundation
import UIKit
class PlayGameView: UIViewController{
var x = Int()
override func viewDidLoad() {
super.viewDidLoad()
print(x)
}
}
here i have added both the view controller from the object library and not working with the default one which is present in storyboard by default. i dont know why these two viewcontroller are not working. please help.
Regards Dev
One solution would be to write the data out to NSUserDefaults and then read it back from NSUserDefaults in the other view controller. Probably not the proper or correct way to share data between two view controllers, but it's been a reliable work around for me.
Other than that, you'd need to share your code so that we can see what's occurring.
Can you post also the code in your controllers C & D. And also if you have copy/paste the code inside your first two controllers into the two others, are you sure that in your prepareForSegue method you have changed the name of the destination segue ?
Assuming you have created the segue in Storyboard:
All you need is to do is put all of needed updates in prepareForSegue because twotofive is called after prepareForSegue.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
value = 1
let nextView : PlayGameView = segue.destinationViewController as! PlayGameView
nextView.x = value
}
Since you have connected your segue from button click to view controller, when you press button segue is automatically called. Instead of connecting segue from button to VC, connect VC to VC. Then in button click method at the last add below line:
#IBAction func twotofive(sender: AnyObject) {
value = 1
self.performSegueWithIdentifier("<Name of the segue identifier>", sender: self)
}
This will call your prepareForSegue. If you are calling more then one VC using segue from a VC then you can use segue.identifier to check which VC was called as below
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "CVC" {
}
I am getting "unexpectedly found nil while unwrapping an Optional value" because in my code below I am trying to assign value to webview before its initialize. I am trying to transition from Master to Detail view controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject
let controller = (segue.destinationViewController as UINavigationController).topViewController as DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
Detail View Code:
var detailItem: AnyObject? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let detailContent = detailItem?.valueForKey("content") as? String{
self.webView.loadHTMLString(detailContent as String, baseURL:nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
}
It is failing because my Webview in Nil. How do I come around this situation where my outlets are not initialized while setting them.
Please help.
Thanks.
Stop and think about the order in which things happen:
prepareForSegue - The destination view controller exists, but that's all. It has no view and its outlets have not been set. You can set its non-outlet properties but that's all you can do.
The segue starts to happen.
The destination view controller gets viewDidLoad. Now it has a view and its outlets are set.
The segue completes and the destination view controller gets viewWillAppear: and later, viewDidAppear:. Now its view is actually in the interface.
So clearly you cannot permit configureView to assume that the web view exists, because the first time it is called, namely in prepareForSegue, it doesn't exist. configureView needs to test explicitly whether self.webView is nil, and if it is, it should do nothing:
func configureView() {
// Update the user interface for the detail item.
if self.webView == nil { return } // no web view, bail out
if let detailContent = detailItem?.valueForKey("content") as? String{
self.webView.loadHTMLString(detailContent as String, baseURL:nil)
}
}
After that, everything will be fine. viewDidLoad will subsequently be called, and configureView will be called again - and this time, both detailItem and the web view exist, so all will be well.