i have the following problem:
I have a storyboard containing three views with UIViewController classes. I do not use seques and apart from initial view (call it View A), all other views are created programmatically with presentViewController method. The problem i have is that when A opens B with presentViewController and then B opens C with presentViewController, when i dissmiss C with dismissViewControllerAnimated (in C there is a Button with Outlet to C UIViewController calling dismissViewControllerAnimated on self), C View disapears, and B appers but only for a 0.1 sec, and then i get C View showing again, and after that the close button wont work anymore.
Any idea what the reason might be?
Best Regards Edin
//delegate definition used between controller A/B and B/C
#protocol ParentControllerDelegate <NSObject>
//called as delegate method from B on A and from C on B to dismiss B from A and C from B
- (void)dismissView:(UIViewController*)controller;
#end
// MainMenuViewController.h which is controller A
#interface MainMenuViewController : UIViewController <ParentControllerDelegate>
//Controller B property
#property (strong, nonatomic) ChooseLevelViewController *chooseLevelViewController;
//button to open controller B
#property (weak, nonatomic) IBOutlet UIButton *chooseLevelBtn;
#end
//MainMenuViewController.m - Controller A
#implementation MainMenuViewController
//called to present chooseLevelViewController which is controller B
- (IBAction)chooseLevelPressed:(id)sender
{
if(self.chooseLevelViewController == nil)
{
self.chooseLevelViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ChooseLevelView"];
self.chooseLevelViewController.parentControllerDelegate = self;
}
[self presentViewController:self.chooseLevelViewController animated:YES completion:nil];
}
//called from controller B over delegate mechanism to dismiss B
- (void)dismissView
{
[self.chooseLevelViewController dismissViewControllerAnimated:YES completion:nil];
}
#end
// ChooseLevelViewController.h which is controller B
#interface ChooseLevelViewController : UIViewController <ParentControllerDelegate>
//Controller A as delegate
#property (assign, nonatomic) id <ParentControllerDelegate> parentControllerDelegate;
//Controller C property
#property (strong, nonatomic) ChoosePlayerViewController *choosePlayerViewController;
//button to dismiss B over delegate A
#property (weak, nonatomic) IBOutlet UIButton *backMainBtn;
//button to open C
#property (weak, nonatomic) IBOutlet UIButton *choosePlayerBtn;
#end
//MainMenuViewController.m - controller B
#implementation ChooseLevelViewController
//calling controller A as delegate to dismiss B
- (IBAction)backMainBtnPressed:(id)sender
{
[self.parentControllerDelegate dismissView];
}
//presenting choosePlayerViewController which is controller C
- (IBAction)choosePlayerBtnPressed:(id)sender
{
if(self.choosePlayerViewController == nil)
{
self.choosePlayerViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ChoosePlayerView"];
self.choosePlayerViewController.parentControllerDelegate = self;
}
[self presentViewController:self.choosePlayerViewController animated:YES completion:nil];
}
//called from controller C over delegate mechanism to dismiss C
- (void)dismissView:(UIViewController*)controller
{
[self.choosePlayerViewController dismissViewControllerAnimated:YES completion:nil];
}
#end
//ChoosePlayerViewController.h which is controller C
#interface ChoosePlayerViewController : UIViewController
//Controller B as delegate
#property (assign, nonatomic) id <ParentControllerDelegate> parentControllerDelegate;
//button to dismiss C over delegate B
#property (weak, nonatomic) IBOutlet UIButton *closeBtn;
#end
#implementation ChoosePlayerViewController
//calling controller B as delegate to dismiss C
- (IBAction)closeBtnPressed:(id)sender
{
[self.parentControllerDelegate dismissView];
}
#end
You should NEVER have a view dismissing itself. You should always have the parent view dismiss the child.
B should dismiss C
A should dismiss B
Check http://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/ModalViewControllers/ModalViewControllers.html#//apple_ref/doc/uid/TP40007457-CH111-SW14
for more information.
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
i tried using storyboard instantiateViewControllerWithIdentifier and it was working fine but i needed delegate method so i added below code when opening
SecondViewController from FirstViewController,
SecondViewController *svc=[[SeconViewController alloc] init];
svc.delegate=self;
[self.navigationController pushViewController:svc animated:YES];
but it opens black screen.any sort help is really appreciated. Thank you.
secondViewController.h file
#protocol sendDataToAudioDelegate <NSObject>
-(void)sendData:(NSMutableArray *) data;
#end
#interface AudioSavedFilesViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) IBOutlet UITableView *fileTable;
#property (strong, nonatomic) NSMutableArray *selectedIndices;
#property (strong, nonatomic) AVAudioPlayer *audioplayer;
- (IBAction)selectedFiles:(id)sender;
#property (nonatomic,assign)id <sendDataToAudioDelegate> delegate;
#end
Here:
SecondViewController *svc= [self.storyboard instantiateViewControllerWithIdentifier #"identifier"];
svc.delegate=self;
You are trying to set SecondViewController's delegate property. But the error you are getting when doing so:
property delegate not found on object type 'UIViewController'
Suggest that your SecondViewController class does not have a property called delegate. Hence you need:
#interface SecondViewController
#property (nonatomic, assign) id delegate;
#end
The reason is because you're creating a completely new instance of SecondViewController and pushing it onto the navigationController. You're not using the storyboard scene at all, thus, all the UI elements you added and connected up won't work either. You'll need to revert to how you were doing it with instantiateViewControllerWithIdentifier
You can still use the delegate as you're doing so there like this:
SecondViewController *svc= [self.storyboard instantiateViewControllerWithIdentifier #"identifier"];
svc.delegate=self;
[self.navigationController pushViewController:svc animated:YES];
Edit
After reading your comments...
I think you haven't set the scene in your storyboard to use the SecondViewController.
So you need to replicate this:
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 need to dismiss keyboard which show itself when I click UITextField from childView. I need to do this in method (scrollViewWillBeginEditing) which is in parentViewController. How can I do it?
EDIT:
I have method in childViewController:
- (void)dismissKeyboard {
[self.textField resignFirstResponder];
NSLog(#"%#", self.textField.text);
}
and .h of childViewController:
#protocol VVInformationTableViewControllerDelegate<NSObject, UIScrollViewDelegate>
-(void)dismissKeyboard;
#end
#interface VVInformationTableViewController : UITableViewController <UITextFieldDelegate, UITableViewDelegate, UIScrollViewDelegate, VVInformationTableViewControllerDelegate>
#property (weak, nonatomic) id<VVInformationTableViewControllerDelegate> delegate;
and I try to call it in:
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
[self.infoTableController dismissKeyboard];
}
When it is call then NSLog print (null) and keyboard didn't dismiss, but when I call dismissKeyboard from childViewController then it print true value and keyboard dismiss.
Any help?
You can use the following code
[self.childView endEditing:Yes];
I have this simple project where, when a button on a second ViewController (SecondViewController) is pressed a Delegate function send a string to the first ViewController and Unhide a new button which is not visible.
I have no problem now doing this however, if I add a third view between these to windows the function stops working.
Here what I mean:
In order to do this I use
In ViewController.h:
#interface ViewController : UIViewController <SecondViewControllerDelegate>
In ViewController.m I implemented a Segue function which detected an Identifier and send the value to second view :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"to2"]) {
SecondViewController *SecondView = segue.destinationViewController ;
SecondView.delegate = self;
}
}
-(void)done:(UIButton*)name{
[self dismissViewControllerAnimated:YES completion:nil];
NSLog(#"back to first view, name=%# ", name);
level2But.hidden = NO;
}
To return to the first window i have created a returnButton in SecondViewController.h:
#protocol SecondViewControllerDelegate <NSObject>
-(void) done:(UIButton*)returnButton; //variable passed to ViewController
#end
#interface SecondViewController : UIViewController{
IBOutlet UIButton *returnButton;
id delegate; //declare delegate as an object
}
#property (nonatomic, strong) id <SecondViewControllerDelegate> delegate;
-(IBAction)returnButtonPressed:(id)sender;
In SeconViewController.m I have:
-(IBAction)returnButtonPressed:(id)sender{
[self.delegate done:returnButton];
}
This code is working just fine However if I had a third View It stops Working.
This is not working anymore.
AnySuggestion? Any Help?
This is the project file:
http://goo.gl/3rJOje
I did checked your code and I could see the self.delegate is nil and hence you are not able to move in SecondViewController.m.
-(IBAction)returnButtonPressed:(id)sender{
[self.delegate done:returnButton];
}
so make sure you pass the delegate.
Also if you want to know how to pass the data(delegate) then go through the below tutorial :
http://www.appcoda.com/storyboards-ios-tutorial-pass-data-between-view-controller-with-segue/