Passing data between two different storyboards - ios

I have two questions.
1. First question:
In my project, I have two different storyboard files: A storyboard and B storyboard.
I want to pass data (nsstring) from (A) controller of (A) storyboard to (B) controller of (B) storyboard
how I make it?
2. Second question:
In the second storyboard, I have two controllers chained with a segue.
When I call segue in the code with this instruction:
[self.navigation controller performSegueWithIdentifier: #"secondViewSegue" sender:self];
I have a message: "has no segue with identifier 'secondViewSegue' "
Why?

1/ A good way to do this is to make a separate model object that can be equally addressed from both locations. And the simplest way to do that is to add a property to the #interface section of your AppDelegate.h file, eg
#property (nonatomic, strong) NSString* sharedString;
To set and get it, you need to add typed access to the AppDelegate in any file that needs it:
#include AppDelegate.h
Then you can use...
AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
//to set (eg in A controller)
appDelegate.sharedString = string;
//to get (eg in B controller)
NSString* string = appDelegate.sharedString;
As an alternative to a property, you could use a static variable in a header file:
static NSString* staticString;
Which would be accessible to any object that #imports the header file. Not really the Objective-C way though.
For more elaborate cases, you may want to create a singleton object to access your model data.
2/ Try:
[self performSegueWithIdentifier: #"secondViewSegue" sender:self];
Ensure that the Segue is wired from your viewController, not it's Navigation Controller.

There are various ways to pass data between two different view controller in different storyboard. What I am using, I would like to share
Swift 3
let storyboard = UIStoryboard(name: "StoryboardName2", bundle: nil)
let controller :NextViewController = storyboard.instantiateViewController(withIdentifier: "NextVCIdentifier") as! NextViewController
controller.NameofUser = Self.SelectedUser
Declare variable in next viewcontroller as
public var NameofUser:String = ""

Related

ios Delegate Objective-c data sending from 2nd view controller to 1st view controller

I have 2 view controllers . First one is UIview controller and second one is table view controller.
I want to send data 2nd (table view controller) to first(uiview Controller) after the selection of rows of 2nd view controller.
For this i have written a delegate protocol.
But my delegate protocol is not working...
I figured out the problem.The object of second view controller that i am creating.
address = [[second_viewcontroller alloc] init];
address.delegate = self;
is different from self of second_viewcontroller view controller page.
How to make this two same.
self = [[second_viewcontroller alloc] init];
your problem my delegate protocol is not working... I figured out the problem.The object of second view controller that I am creating. address=[[second_viewcontroller alloc]init]; address.delegate=self; is different from self of second_viewcontroller view controller page.
It's clear say that way you create the second_viewcontroller object is not right.
You have to create the object from ViewController storyboard identifier.
First give the Storyboard ID to ViewController from Storyboard.Follow this step to Giving the Storyboard ID.
Select the particular ViewController in Storyboard.
Go to IdentityInspector.
Under IdentityInspector, There is identity section and add the Storyboard ID In "Storyboard ID" Field.
Syntax For Creating a ViewController Object.
Second_viewController *aVC = [self.storyboard instantiateViewControllerWithIdentifier:#"Second_viewController"];
aVC.delegate = self;
I assume that you are calling the Second_ViewController from storyboard instead of doing programmatically.
In that case, the correct instance of Second_ViewController can be accessed in prepareForSegue. For that, you need to set a Storyboard segue identifier, eg "Second_ViewController"
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"Second_ViewController"]) {
SecondViewController *aSecVC = segue.destinationViewController;
// Register the Delegate to self.So when we call the delegate method from secondVC, SendMessage will be call of ViewController
aSecVC.delegate = self;
}
}
If you use alloc-init or instantiateViewControllerWithIdentifier, when you are using a storyboard push segue, it will create another instance.
Yes as your instantiating a new instance of the second view controller. From what I could make out from your question, I guess if you obtain the instance of your secondViewController from the Navigation Stack it should work
I created a sample project for you to get the basic knowledge of how to pass data backward using NSUserDefaults. try this in GitHub. hope this will help to you. project url Pass data backward using NSUserDefaults in Objective-C

how to call function from other VC?

I have some variable lets say isMenuVisible = false; I want to set some function when this var is changed:
isMenuVisible: Bool!{
didSet{
callFunctionFromOtherViewController()
}
}
How is that possible? Do I need to create instance of VC in order to access that function? Or I need to make that function public?
To call a method/function from another VC (or from any other class), you have 2 choices:
Create a class method in this other view controller:
In your MyViewController.h
+(void)myClassMethod;
Wherever you need to use it
[MyViewController myClassMethod];
Create an instance method in this other view controller:
In your MyViewController.h
-(void)myClassMethod;
Wherever you need to use it
MyViewController *myViewControllerInstance = [[MyViewController alloc] init];
[myViewControllerInstance myClassMethod];
definitely no need to create instance for the viewController,
if it is a rootViewController you can get it like that :
var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var vc = (appDelegate.window?.rootViewController as? MyViewController)!
if your view controller is parent, it is like that:
var myViewfromParent = self.parentViewController as? MyViewController
if you use storyboard with id, you can get it like that:
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! UIViewController
hope it helps
If you're using viewController containment and you can easily reference the view controller you can simply create a public method in your .h file and call that method... however, with viewControllers it's often the case that a. you don't have / want universal access to the viewController instance in other classes and b. the viewController may not even exist when this method is changed. For these reasons I'd go with NSNotifications when triggering actions in viewControllers.
NSNotificationCenter addObserver in Swift
If this isn't acceptable and you can guarantee that this class will be linked in an architecturally sound way, I would create a delegate. That way you can set the delegate when you create the viewController and there's no mess when trying to get back to your viewController... I'd only recommend this if there's a genuine connection between the viewController and the class that contains the action (in this case the bool)
In objective c you'd do
MyViewController *myViewController = [[MyViewController alloc] init];
myViewController.delegate = theClassContainingTheBool; // could be self.. might not be... If you can't do this line of code, then you should use notifications
In swift
Delegates in swift?
How to define optional methods in Swift protocol?

Instantiate View Controller from Storyboard vs. Creating New Instance

What is the functional difference between instantiating a View Controller from the storyboard and creating a new instance of it? For example:
#import "SomeViewController.h"
...
SomeViewController *someViewController = [SomeViewController new];
versus
#import "SomeViewController.h"
...
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
SomeViewController *someViewController = [storyboard instantiateViewControllerWithIdentifier:#"SomeViewController"];
In either case, is someViewController effectively the same thing?
The main difference is in how the subviews of your UIViewController get instantiated.
In the second case, all the views you create in your storyboard will be automatically instantiated for you, and all the outlets and actions will be set up as you specified in the storyboard.
In the first, case, none of that happens; you just get the raw object. You'll need to allocate and instantiate all your subviews, lay them out using constraints or otherwise, and hook up all the outlets and actions yourself. Apple recommends doing this by overriding the loadView method of UIViewController.
In the second case, the view controller will load its view from the storyboard and you will be happy.
In the first case, it won't. Unless you've taken other steps (like overriding loadView or viewDidLoad or creating a xib named SomeViewController.xib), you'll just get an empty white view and be sad.
In Swift you can do the same with,
var someVC = self.storyboard?.instantiateViewControllerWithIdentifier("SomeViewController") as! SomeViewController
You will need to give the Identifier in the Storyboard to the SomeViewController and tick the checkmark to Use Storyboard ID
It is not the same thing. In the storyboard you probably have some UI elements laid out. They might have constraints and properties setup through the storyboard. When you instantiate the viewcontroller via the storyboard, you are getting all the instructions for where those subviews are and what their properties are. If you just say [SomeViewController new] you are not getting all the instructions that the storyboard has for the view controller.
A nice test will be to add a UIViewController to a storyboard and drag a red view onto it. Instantiate it using both methods and see what the differences are.
simple swift 3 extension
fileprivate enum Storyboard : String {
case main = "Main"
}
fileprivate extension UIStoryboard {
static func loadFromMain(_ identifier: String) -> UIViewController {
return load(from: .main, identifier: identifier)
}
static func load(from storyboard: Storyboard, identifier: String) -> UIViewController {
let uiStoryboard = UIStoryboard(name: storyboard.rawValue, bundle: nil)
return uiStoryboard.instantiateViewController(withIdentifier: identifier)
}
}
// MARK: App View Controllers
extension UIStoryboard {
class func loadHomeViewController() -> HomeViewController {
return loadFromMain("HomeViewController") as! HomeViewController
}
}
In case you don't want to instantiate a new VC using instantiateViewControllerWithIdentifier but accessing the instance created by the storyboard from the AppDelegate:
create a property in AppDelegate.h so it will be accessible from classes using it
#property (nonatomic, strong) myViewControllerClass*vC;
in viewDidLoad inside myViewControllerClass.m I access the shared instance of AppDelegate and feed the property with self: [AppDelegate sharedInstance].vC = self;
I had to use this solution in a complex storyboard and still can't get over the fact that I cannot find an easy way to access all (or at least the ones I need) objects in storyboard simply by addressing their identifiers.
another thing to check for is if the viewcontroller that's throwing the error has a storyboardIdentifier, you can check the storyboard xib file.
the identifier was missing in my case, the error stopped when i added it

Send UITableView row From One tableviewController to another

I want to send a UITableViewCell at indexPath.row from one controller to another. I can remove the row using removeObjectAtIndex, but unable to send the removed row into another controller.
I'm trying to store the removed row in an NSMutableArray in one controller, but don't know how to populate it in another controller.
Below is the code -
ViewController *view= [self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
view.anotherviewArray= [self.arrayFromAFNetworking objectAtIndex:indexPath.row];
If anyone can give me an idea, it would be helpful.
I believe it's bad practice to retain UI elements and pass them around your app. You should instead have some kind of a model containing your data, and pass this model from one view controller to the other. I'd recommend checking out tableview frameworks such as the free Sensible TableView framework, as they do an excellent job of providing such a model for you automatically.
I personally think that it's wrong approach to pass UI object as parameter to another controller.
As I would do it is create some object that encapsulates data model from this cell and pass this object to another view controller.
#interface DataObject : NSObject
#property id field1;
#end
UI part of cell can be easily copied in Interface Builder, so I don't see problem in that. Probably it would be great to have cell class that could fill necessary field from the object with data. This class you can use in both view controller that have to show the same cell
#interface CustomTableViewCell : UITableViewCell
- (void)customizeCellWithDataObject:(DataObject *)dataObject;
#end
I hope it makes sense to you
Assuming that you DO want to set the other data source with only this row, you need to pass it as an array.
view.anotherviewArray= [NSArray arrayWithObject:[self.arrayFromAFNetworking objectAtIndex:indexPath.row]];
But it's hard to tell from the little code that you have provided. I assume that since you are instantiating the viewController you are also transitioning to it below the provided code. If you are trying to set the array for a viewController already presented, you need to access that one, not create another, perhaps by having saved a reference to it an ivar within the current viewController or another accessible class.
I would also not name a ViewController view, it is confusing to anyone reading the code later on.
Editing for my comment below about traversing the hierarchy. Here is some code that I used in one iPad project to return the final presented viewController. This method is in the appDelegate. It is somewhat specific to my project, where there is only one navigationController. But you can adapt it to yours. You would test for a viewController that is of the class of your target view controller.
- (UIViewController *)topViewController {
UIViewController *rootViewController = self.window.rootViewController;
UIViewController *topViewController = rootViewController.presentedViewController;
while (topViewController && topViewController.presentedViewController) {
topViewController = topViewController.presentedViewController;
if ([topViewController isKindOfClass:[UINavigationController class]]) {
UIViewController *presentedViewController = [(UINavigationController *) topViewController topViewController];
if (presentedViewController) {
topViewController = presentedViewController;
}
}
}
return topViewController;
}
The other approach is to set a property to it when it is created and presented. We don't have enough code to get a good idea of your app as a whole. Where are you creating the ViewController instance that you are displaying? By that I mean where you are calling a segue to it, or pushing it onto a navigationController or call presentViewController. Wherever that is, you need to set a property or ivar to it. Let's say that you use a property in your appDelegate as a very generic case.
In your MyAppDelegate.h file you have
#property(nonatomic,strong) ViewController *viewController;
Wherever you first create it you set that property
MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
I now think you are trying to add this to a mutableArray in the other ViewController. Then replacing your code from the tableViewCell above you would use
MyAppDelegate appDelegate = (MyAppDelegate)[[UIApplication sharedApplication] delegate];
[appDelegate.viewController.mutableDataArray addObject:self.arrayFromAFNetworking objectAtIndex:indexPath.row];
[appDelegate.viewController.tableView reloadData];
I will say that it is not great practice to use the appDelegate for the property. But it is a generic answer that would work. It's best to put it in a class which is common to the viewControllers that you are passing data between. Perhaps a single parent which holds these two viewControllers?

instantiateViewControllerWithIdentifier and pass data

I am using Storyboard in my app and I want to pass data from one view to another view.
Instead of using segues I am using instantiateViewControllerWithIdentifier. In this case I am instantiate from my first TableViewController to a NavigationController which has a second TableViewController attached because I need the navigation in the second TableViewController. Now I want to pass data from my first TableviewController, depending which row was clicked, to my second TableviewController. In this case newTopViewController would be my NavigationController but my problem is now how to pass data from firstTableViewController to the secondTableviewController.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = [NSString stringWithFormat:#"%#Top", [menuArray objectAtIndex:indexPath.row]];
UIViewController *newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
}
If you instantiate a navigationController, you can use the viewControllers property to get the inner viewController of the navigation controller.
Something like this:
UINavigationController *navigationController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
MBFancyViewController *viewController = navigationController.viewControllers[0];
// setup "inner" view controller
viewController.foo = bar;
[self presentViewController:navigationController animated:YES completion:nil];
newTopViewController.anyVariableToShow= anyVariableToSend;
I do this pretty often on a few of my apps...
//Create new VC
CookViewController *detailViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"CookVC"];
//Set recipe
[detailViewController setRecipe:recipe];
//Pop over VC (can be pushed with a nav controller)
[self presentPopupViewController:detailViewController animationType:MJPopupViewAnimationFade];
If you aren't using a navigation controller or segues, then I think you need to reconsider your app design.
Actually it's not just a data pass problem as this is a program control and data transfer question together.
Even you would have to rethink about your app's concept, as you'd like to use storyboard without the meaning of storyboard, it's up to you and I hope you have good reason to do what you do.
So when you decided not to use segue you lost the new and comfortable way of instantiating a new controller and transferring data with it and you have to do the transfer of control and the data in two distinct steps. When you instantiate another scene in storyboard (like you do with instantiateViewControllerWithIdentifier:) you just instantiated a new controller and transferred the control but not the data. Just think about it as you instantiated a new controller from a xib in an old way (so you have to use initWithCoder: or awakeFromNib in the second view controller as the storyboard will not call initWithName:bundle:), but did not do anything more.
So you will have a new controller (it named in the identity part of the second storyboard) which is hanging in the universe without any relationship or connection with anything else (as the storyboard picture illustrates it nicely) and you could do with it what you'd like.
So you'd like to do something and you need data from the previous storyboard (ViewController). What you need is making available those data to the second storyboard(ViewController), and as you know there are lot of solution for this which were available long time before even storyboard is existed.
So regarding your code, the "data transfer" is depending on your design, whether the two controllers are subclasses of each other or whatsoever...
If you don't like to deal with subclassing and like to decoupling them as much as possible, the best way just make a property of your data in the first controller and refer to them from the second (after importing the first's .h file) and just refer to it in it's viewDidLoad or in initWithCoder: or anywhere where you need them, as
secondViewControllerdata = firstViewControllerdata.thatDataProperty
Of course you can do the same in reverse and make a property of the second controller and refer to it in your first view controller.
You can define some parameter in UIViewController to receive data:
#property (assign) int param1;
#property (retain) NSMutableArray *param2;
and use below to pass the data:
[newTopViewController setParam1:XX];
[newTopViewController setParam2:XX];

Resources