I'm doing an Ipad app. Now I have 2 viewcontrollers, ViewController has a button1 which has a popover segue to the second viewcontroller(PopoverController). Then, the PopoverController has a button2, if I click the button2, I'll receive some UIImage from my server. I want to add fews subviews of UIImageView to the ViewController to display these images if I click the button2.
The button1 works well, the PopoverController can pop up as expected. BUT when I click the button2, nothing happend. I want to know how can I pass the data between 2 viewcontrollers and how to add subviews to another one.
Some codes relating to my problem:
ViewController.h:
#import <UIKit/UIKit.h>
#class PopoverController;
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *button1;
#property (strong, nonatomic) PopoverController *popoverController;
#end
PopoverController.h:
#import <UIKit/UIKit.h>
#class ViewController;
#interface PopoverController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *button2;
#property (strong, nonatomic) UIImage *tempImg;
#property (strong, nonatomic) ViewController *viewController;
- (IBAction)addsubviews:(id)sender;
#end
I can not just use [viewController.view addSubview:img1]; in the - (IBAction)addsubviews:(id)sender;method to addsubview. So someone can help me? :)
====1st update====
Someone suggest that I have to use - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender method. I have tried this one by control click the button2 and create a custom segue between button2 and ViewController. When I clicked the button2, it showed :
Terminating app due to uncaught exception 'NSGenericException', reason: 'Could not find a navigation controller for segue 'change'. Push segues can only be used when the source controller is managed by an instance of UINavigationController.'
So I'm wondering whether I should add a NavigationController. If so, what should I do?
====2nd update====
I use Paramasivan 's code now, and I found the way to call method from another viewcontroller. The problem now is the newly added subview in my viewcontroller doesn't show up. I guess I have to update my viewcontroller in order to make it visible.
in my - (IBAction)addsubviews:(id)sender; method, i invoke the method in ViewController by [self.viewController createSubViewWithImage:_tempImg];
so the method can be invoked when i click the button2, but the view of viewcontroller has nothing changed.
Add this in - (void)viewDidLoad,
self.popoverController = [[PopoverController alloc] init];
self.popoverController.viewController = self;
Make sure that in no other places, you are setting self.popoverController = ....
Do NOT add anything like self.viewController = ... in popovercontroller class. And you dont have to do self.viewController.popoverController = self; as well. Just remove these lines if you already have it.
Once these are done, make sure that you are displaying self.popoverController only in the popover and you are not creating a new object for popoverController class there. So if these are fine, you can use any approach you want for passing the image from popoverController class to viewController class.
as you mentioned in your comment you can use [self.viewController createSubViewWithImage:_tempImg]; in your popovercontroller class.
Update:
If you are doing via storyboard, you need to set this in prepareForSegue method and you dont have to create self.popoverController at all. Remove that part in your case. You can follow the procedure mentioned here to set up a custom segue and implement prepareForSegue method to pass the object. Source: On storyboards, views and passing data along
Set the name of segue in storyboard to "CustomSegue"
Implement prepareForSegue method
Inside the method, check if name of segue matches "CustomSegue" and then set the viewController in the popoverController object there as,
Try,
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"CustomSegue"]) {
PopoverController *popoverController = [segue destinationViewController];
popoverController.viewController = self;
}
}
After doing this, you need to call [self.viewController createSubViewWithImage:_tempImg]; in your popoverController class.
Check out the Communicating with Objects doc, there are several ways to do what you want.
In ViewController.h add the following
-(void)createSubViewWithImage:(UIImage *)imageDownloaded {
UIImageView *imageViewTemp = [[UIImageView alloc] initWithImage:imageDownloaded];
[self.view addSubView:imageViewTemp];
}
In PopoverController.h add the following
#property (nonatomic, retain) ViewController *viewControllerPassed;
And after image downloaded, call the following in PopoverController.h
[viewControllerPassed createSubViewWithImage:imageDownloaded];
You can pass data using the below method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ViewController2"])
{
ViewController2 *v2VC = [segue destinationViewController];
v2VC.yourData = self.someStuff;
}
}
Related
I've a TableViewController in which I'm saving the selected cells in an NSMUtableArray. After selecting these cells user clicks on a confirm button and in this button action I'm trying to pass that NSMUtableArray so that I can display it in another viewController tableView
#import <UIKit/UIKit.h>
#protocol SelectedDXDelegate;
#interface AddDXTableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *favDXArray;
#property (strong, nonatomic) UISearchController *searchController;
#property (nonatomic, strong) DX *AddEditDX;
#property (weak) id<SelectedDXDelegate> delegate;
- (IBAction)confirmPressed:(id)sender;
#end
#protocol SelectedDXDelegate <NSObject>
#required
-(void)getSelectedDX:(NSMutableArray *)DXselected;
#end
So when confirm button is pressed
- (IBAction)confirmPressed:(id)sender {
[self.delegate getSelectedDX:selectedDX];
[self.navigationController popViewControllerAnimated:YES];
}
it gets me to the
-(void)getSelectedDX:(NSMutableArray *)DXselected
{
myDXSelected = DXselected;
}
But it crashes the app here at reloadData in
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.DXTableView reloadData];
}
You seem to have mixed up which view controller should be the delegate and which is the delegator. Also, you are just allocating a new instance of the AddDXTableViewController and assigning this as the delegate. This won't work; you need to have the existing instance of your view controller set as the delegate.
From what I can tell from your question, it is actually an instance of DXViewController that is to be the delegate of AddDXTableViewController
Presumably in DXViewController you have some code something like:
AddDXViewController *newViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddDXViewController"];
[self.navigationController pushViewController:newViewController animated:YES];
What you need to do is set your delegate at this point:
AddDXViewController *newViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddDXViewController"];
newViewController.delegate = self;
[self.navigationController pushViewController:newViewController animated:YES];
Having said all of that, since you are using a storyboard, a delegate is probably an unnecessarily complicated way of achieving your requirement; You can use a segue to move between the first and second view controller and an unwind segue to return back to the first. You can then implement prepareForSegue in the second view controller and use that to provide the array back to the first view controller
You have to make the UIViewController added in storyboard to AddDXTableViewController type in the identity inspector tab in story board.
See here the image
Here you can see the class type is ViewController, click on the dropdown and select the type to AddDXTableViewController
then type cast the viewController to AddDXTableViewController. As per my guess the instantiateViewControllerWithIdentifier: returns UIViewController which does not have delegate may cause the crash
AddDXTableViewController *addDXTVC = (AddDXTableViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"DXViewController"];
addDXTVC.delegate = self;
Let me know If it works
How can I communicate with container view controller and parent view controller? I've tried to make a communication and pass the value between them. I've added a container view in the green view controller. And I had set segue name with "communicateSegue". I've searched some methods, I know we can set the prepareForSegue to pass the value at initial.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"communicateSegue"]) {
NSLog(#"call prepare for segue at parent view controller");
}
}
But now I want to pass value and change the label in the different viewcontroller . Not at the initial.
I don't know how to do?
Question:
Have anyone know how to implement ParentViewController pass value to Container View Controller and show the label in the ContainerViewController?
And reverse , Container View Controller click the button then pass the value and change the label show in the ParentViewController with objective-c?
Thanks.
My declare variable in parentViewController.h:
#import
#interface ParentViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *parentTF;
#property (weak, nonatomic) IBOutlet UIButton *passValToChildVCBtn;
#property (weak, nonatomic) IBOutlet UILabel *showContainerValLB;
#property (weak, nonatomic) IBOutlet IBAction *passValueToContainerEvent;
#end
In the ContainerChildViewController.h
#import
#interface ContainerChildViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *showParentValLB;
#property (weak, nonatomic) IBOutlet UITextField *childTF;
#property (weak, nonatomic) IBOutlet UIButton *passToParentBtn;
#property (weak, nonatomic) IBOutlet IBAction *passValueToParentBtnEvent;
#end
test project in the here.
thank you very much.
Regarding passing value to the containerView controller, you can make a property in the ChildViewController of the value you want it to be passed. Then in your ParentViewController do something like the following:
self.ChildViewController.yourProperty = yourValue
The opposite can be done in 4 ways:
You can make a delegate protocol to communicate the data between your controllers.
You can post a notification in your ChildViewController and add the parent controller as an observer.
You can use KVO.
And the easiest way out, you can make a property in your parentviewController and access it like the following
((YourParentViewControllerClassType *)self.parentViewController).yourParentProperty = TheValueYouWant;
I do it like this:
set identifier for segue from your containerView to viewController
make property/variable myChildViewController in your parent view controller and property/variable myParentViewController in your child view controller
in your prepareForSegue method set this properties to be the reference to right objects
Now you can access to properties of parent/child view controller through this references.
Example in Swift:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "YourIdentifier" {
// Set references
myChildViewController = segue.destinationViewController as! YourChildViewController
myChildViewController.myParentViewController = self
}
}
Example in Objective C
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"YourIdentifier"]) {
// Set references
myChildViewController = [segue destinationViewController];
myChildViewController.myParentViewController = self
}
}
I did't try the objective C code becouse I don't ever use it. It is just an example to get the idea.
After that you can set any property of parent from child view controller as:
myParentViewController.property = value
and in your parent view controller:
myChildViewController.property = value
Hope this helps anyone!
Is pretty simple, the UIStoryboardSegue contains a reference to your destination view controller using the property -destinationViewController.
In the method -prepareForSegue you can access it and pass the data that you need.
If you need to pass data from child to parent, I strongly suggest you to avoid hard coupling and use something such as delegation or notification pattern.
Remember that your parent view controller already contains a reference to you child view controllers and you can simply access it by using -childViewControllers property.
you can use NSNotificationCenter for updating all screens or you can do this way..
create a property in ContainerChildViewController.h
#property(nonatomic,copy)NSString * mytextString;
this property will be accessible in your ParentViewController.h if you import ContainerChildViewController.h
you have to create a NSString in ParentViewController.h
in this you can set button action to copy textfielddata.text
then you can assign this value to your ContainerChildViewcontroller's
as
// create an instance of 2nd view controller as second in ParenViewController
UIStoryboard *mystoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ContainerChildViewController * second= [mystoryboard instantiateViewControllerWithIdentifier:#"ContainerChildViewController"];
second.mytextString = self.mytextString;
// either you directly pass this value or use the string it should work both ways
NSLog(#"%#",second.orderID);
[self.navigationController pushViewController:second animated:YES];
Reply if it works
try this
- (IBAction)showDetail:(UIButton *)sender {
DetailViewController *detailVc = [self.childViewControllers firstObject];
detailVc.lable.text = sender.titleLabel.text;
}
I am passing textfield data between two view controllers. i have two view controllers Firstviewcontroller, Secondviewcontroller. i have embed them in navigation but when i click the button on first view controller, second view controller is not showing only black screen is showing.below is my code.
First view controller.h
#import <UIKit/UIKit.h>
#import "secondViewController.h"
#interface FirstViewController : UIViewController<SecondViewControllerDelegate>
#property(nonatomic) secondViewController *secondview;
- (IBAction)btnclick:(id)sender;
#property (strong, nonatomic) IBOutlet UITextField *textfield1;
#end
First view controller.m
- (IBAction)btnclick:(id)sender {
secondViewController *SecondViewController= [[secondViewController alloc]init];
SecondViewController.data=self.textfield1.text;
[self.navigationController pushViewController:SecondViewController animated:YES];
}
- (void)dataFromController:(NSString *)data
{
[self.textfield1 setText:[NSString stringWithFormat:#"%#",data]];
}
Seconviewcontroller.h
#protocol SecondViewControllerDelegate <NSObject>
#required
- (void)dataFromController:(NSString *)data;
#end
#interface secondViewController : UIViewController
#property (nonatomic, retain) NSString *data;
#property(nonatomic,weak)id<SecondViewControllerDelegate> _delegate;
#property (strong, nonatomic) IBOutlet UITextField *textfield2;
#end
Seconviewcontroller.m
- (void)viewDidLoad {
[super viewDidLoad];
self.textfield2.text=[NSString stringWithFormat:#"%#",_data];
// Do any additional setup after loading the view.
}
whats the reason ??
You have to either use storyboard and let it intanciate the view controller for you:
[self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"]
Or you can use xib files and tell the view controller which one to load:
[[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil]
second view controller is not showing only black screen is showing
Typically, this means that the view controller has no view. Your code does not explain where secondViewController is expected to get its view from. Is there a secondViewController.xib? If there is, is it correctly configured with a view outlet? If not, what does this view controller do in order to get its view?
Note that you have done a very odd thing with capitalization: you have given your class a small letter (secondViewController), while your instance has a capital latter (SecondViewController). This is backwards and a big mistake, and you should correct it immediately. Perhaps the problem is just a capitalization problem; if you call your class secondViewController and your xib file SecondViewController, that is not a match and they won't find each other.
I see "IBOutlet" in your code, it means you created the user interface of secondviewcontroller on storyboard. I guess [[secondViewController alloc]init] does not get UI element from storyboard then the black screen is shown. You have to create instance of secondViewController by loading from storyboard file.
Basically:
you can get storyboard via storyboard file name
on storyboard, Secondviewcontroller has an storyboard id which is set in Identity Inspector and you can use it to get instance of Secondviewcontroller
You can refer this link for more detail
loading iOS viewcontroller from storyboard
Hope this can help!
I have checked previous queries all but they are different or too advanced for me. I have buttons done programatically so I need to segue using program as well. I am not sending anything, just to go to the next ViewController. Documentation said if not passing anything just use: - (id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(UIViewController *)destination. But I don't understand plus (+) or class yet. Also I cannot just put gjRelaysViewController as destination, Xcode says it is not declared. It is correct for me to create an instance? I have tried prepare for segue but nothing came out of it. Thanks.
Source: gjViewControllers
#property (nonatomic, strong) UIStoryboardSegue *relaysSegue;
#property (nonatomic, strong) gjRelaysViewController *relays;
[_pushButton1 addTarget:self action:#selector(relayCircuitsOn:) forControlEvents:UIControlEventTouchDown];
-(void)relayCircuitsOn: (id) sender
{
_relaysSegue = [[UIStoryboardSegue alloc] initWithIdentifier:#"toRelays" source:self destination: _relays];
}
Destination: gjRelaysViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if(![segue.identifier isEqual: #"toRelays"])
{
return;
}
}
Ctrl-Drag in InterfaceBuilder from ViewController 1 to ViewController 2 to create a segue. Click on the segue and give it a name in the right pane.
In your code in relayCircuitsOn call
[self performSegueWithIdentifier:#"Insert the name you specified above" sender:self];
and you should be all set.
Alternatively you can do the transition in code:
Import ViewController 2 in ViewController1.m `#import "ViewController2.h"
Create a new instance of ViewController2: ViewController2 *vc = [[ViewController2 alloc] init];
Push it to the navigation stack (if you have one?): [self.navigationController pushViewController:vc animated:YES];
I'm currently trying to have a better understanding on how the mechanisms of passing data between controllers work and I'm a little confused especially when passing data back from a second view controller to the main view controller.
This is what I have that works but don't fully understand. I have two view controllers, in the first one I have a button that when clicked it basically goes to the second view controller and a label which shows a message sent from the second view controller. In the second view controller I have a button and a textField, the button basically sends whatever is in the textfield to the label in main view controller.
Here is the code...
// FirstVC.h
#import <UIKit/UIKit.h>
#import "SecondVC.h"
#interface FirstVC : UIViewController <passNames>
#property (nonatomic, strong) NSString* firstNameString;
#property (weak, nonatomic) IBOutlet UILabel *firstNameLabel;
#end
//FirstVC.m
#import "FirstVC.h"
#implementation FirstVC
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier]isEqualToString:#"secondController"])
{
UINavigationController *navController = segue.destinationViewController;
SecondVC *vc2 = (SecondVC*)navController.topViewController;
[vc2 setDelegate:self];
}
}
-(void)viewWillAppear:(BOOL)animated
{
self.firstNameLabel.text = _firstNameString;
}
-(void)setFirstName:(NSString *)firstName
{
_firstNameString = firstName;
}
#end
//SecondVC.h
#import <UIKit/UIKit.h>
#protocol passNames <NSObject>
-(void)setFirstName:(NSString*)firstName;
#end
#interface SecondVC : UIViewController
#property (retain)id <passNames> delegate;
- (IBAction)send:(UIBarButtonItem *)sender;
#property (nonatomic, strong) NSString *firstNameString;
#property (weak, nonatomic) IBOutlet UITextField *firstNameText;
#end
//SecondVC.m
#import "SecondVC.h"
#import "FirstVC.h"
#interface SecondVC ()
#end
#implementation SecondVC
- (IBAction)send:(UIBarButtonItem *)sender
{
_firstNameString = _firstNameText.text;
[[self delegate]setFirstName:_firstNameString];
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Can someone explain how the prepareForSegue method works in the above code? The reason for this question is because I added an NSLog and it looks like this method is only called in the transition from main view controller to the second controller. Why is this method needed if it is not called when transitioning from second view controller to main view controller which in my case is what I'm doing? It makes sense to use it when passing data from main view controller to a second controller not on the case shown above.
Can some explain the whole mechanism when passing data back to the main view controller?
FYI, I do understand about protocols and delegation.
Thanks a lot.
In your case, you are setting your delegate method of the second view controller to self in mainViewController in you prepareForSegue. This means that apart from navigating to the SecondViewController, you are implementing the callback mechanism in your main view controller, so that your delegate method gets called when the value is passed from the second view controller and this delegate method collects the value as a parameter to handle it in the main View Controller. You might have set the delegate of VC2 as self inn your prepareForSegue because you are creating the instance of VC2 in this method to navigate to the second controller.
Your goal is to hand back the data, like this:
[[self delegate] setFirstName:_firstNameString];
But you can't do that unless you know who to send setFirstName: to, and the compiler won't let you do it unless you guarantee that whoever you are sending setFirstName: to can accept that message.
That is what prepareForSegue prepares. FirstVC has declared that it adopts the passNames protocol, which means that it implements setFirstName:. And now you are saying:
[vc2 setDelegate:self];
...where self is the FirstVC instance. This solves both problems at once. The SecondVC instance (vc2) now has a delegate (the FirstVC instance), it is the right object to send the info back to, and because its delegate is declared as adopting passNames, we know that SecondVC can actually send setFirstName: to that delegate.
Now to the heart of your actual question: The reason for doing this in prepareForSegue is merely that this is the only moment when the FirstVC instance and the SecondVC instance "meet" one another! There is no other moment when the FirstVC instance has a reference to the SecondVC instance so as to be able to call setDelegate on it in the first place. If you weren't using segues and storyboards, the FirstVC would simply create the SecondVC instance directly - and would set itself as its delegate, just as you do:
SecondVC *vc2 = [SecondVC new];
UINavigationController *nav = [
[UINavigationController alloc] initWithRootViewController: vc2];
[vc2 setDelegate:self];
[self presentViewController: nav animated: YES completion: nil];
This is one reason I don't like storyboards: they muddy the story. It's all so simple and obvious when you don't use them and just do everything directly like this.