I want to display the modalViewController from the 2nd screen of the app, and when I dismiss the controller it should navigate to the 1st screen. The below code works fine in iPhone 4, 5 and iPod Touch but NOT in iPAD. The objective is when I dismiss the modalViewController it shouldn't go back to the second screen, but it should display the first screen.
ShareEmail *shareEmail = [[ShareEmail alloc] initWithNibName:[NSString stringWithFormat:#"%#",xibShareEmail] bundle:nil];
shareEmail.fromWer = #"ownPer";
[self presentModalViewController:shareEmail animated:NO];
[shareEmail release];
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
In share email class
[self dismissModalViewControllerAnimated:YES];
You need to retain your shareEmail view controller - make a synthesized retained property
YourClass.h:
#property (nonatomic, retain) ShareEmail *shareEmailViewController;
YourClass.m:
#synthesize shareEmailViewController;
Then display your modal view controller by:
ShareEmail *shareEmail = [[ShareEmail alloc] initWithNibName:[NSString stringWithFormat:#"%#",xibShareEmail] bundle:nil];
self.shareEmailViewController = shareEmail;
[self presentModalViewController:shareEmail animated:NO];
[shareEmail release];
The retained property will keep the view controller from being deallocated while in use. Then when you dismiss it later you can do:
[self.shareEmailViewController dismissModalViewControllerAnimated:YES];
self.shareEmailViewController = nil;
which will release the retained property and free the memory after you're done with it.
Something interesting is happening. You are presenting a view controller from self, and then you're having the navigationController perform its backwards navigation. At this point, the self I was talking about earlier disappears.
Remove the popViewController method from your presentViewController method.
Also, you'll need to use the ^completion handler method. That's where you should put your navigation controller pop code.
-(void)present {
ShareEmail *email = [[ShareEmail...
//You'll need to get a weak reference to `self` in `email`
email.modalDelegate = self; //you need to make a property in ShareEmail
[self presentViewController:email animated:YES completion:nil];
[email release];
}
-(void)dismiss {
[self dismissViewControllerAnimated:YES completion:nil];
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
}
//in ShareEmail.m
[modalDelegate dismiss];
Related
I am using AddressBookUI Framework for Adding contact, when I tried to pushing this view controller then cancel and done button not working properly, I don't want to present it
Here is my code
ABNewPersonViewController *abnpvc = [[ABNewPersonViewController alloc] init];
[abnpvc setNewPersonViewDelegate: self];
[self.navigationController pushViewController:abnpvc animated:YES];
I am also tried add as subview rather then pushing it but when I am adding as subview then it was not added
As per comment i have tried like
UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:abnpvc];
[self presentViewController:navigation animated:YES completion:nil];
Can anyone help me out why properly not working ?
You can implement that too considering also the other answers and the deprecations to ABNewPersonViewController in iOS 9.
As per your remarks:
cancel and done button not working properly
They are working if you have included the ABNewPersonViewControllerDelegate on interface like this:
#interface ViewController () <ABNewPersonViewControllerDelegate>
Pushing the viewController on navigation stack like this:
ABNewPersonViewController *controller = [[ABNewPersonViewController alloc] init];
controller.newPersonViewDelegate = self;
[self.navigationController pushViewController:controller animated:YES];
And by conforming to the protocol by implementing this method:
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(nullable ABRecordRef)person {
// Trick to go back to your view by popping it from the navigation stack when done or cancel button is pressed
[self.navigationController popViewControllerAnimated:YES];
}
The tricky line is to pop the newPersonController from the navigation stack when either Done or Cancel button are pressed.
Enjoy it
Why can't you just do it as the docs say?
It is recommended that you present a new-person view controller modally.
Use
ABNewPersonViewController *abnpvc = [[ABNewPersonViewController alloc] init];
[abnpvc setNewPersonViewDelegate: self];
[self presentViewController:abnpvc animated:YES completion:nil];
That should work fine.
Edit
On second thought, did you set your delegate correctly and do the implementations get called? I suspect they are not implemented or the delegate is not set correctly.
Apple guideline(IMPORTANT) :: New-person view controllers must be used with a navigation controller in order to function properly. It is recommended that you present a new-person view controller modally.
Add Delegate
#interface ViewController () <ABNewPersonViewControllerDelegate>
Pushing the viewController
ABNewPersonViewController *abnpvc = [[ABNewPersonViewController alloc] init];
[abnpvc setNewPersonViewDelegate: self];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:abnpvc];
[self presentModalViewController:navController animated:YES];
And Now Add Delegate Method
#pragma mark ABNewPersonViewControllerDelegate methods
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person
{
[self dismissViewControllerAnimated:YES completion:NULL];
}
That will work fine.
My UIViewController stack looks as follows:
+------ UIViewController_C (presented)
+---- UIViewController_B (presented)
+-- UIViewController_A (pushed)
When I call -dismissViewController:animated on UIViewController_C, UINavigationController dismisses both UIViewController_C and UIViewController_B together, as per the docs with animation on _C, and none on _B.
What is the most compliant way to dismiss _C only?
try as below
after pushing to UIViewController_A present UIViewController_B as below code.
UIViewController_B *bbp=[[UIViewController_B alloc]initWithNibName:#"UIViewController_B" bundle:nil];
UINavigationController *passcodeNavigationController = [[UINavigationController alloc] initWithRootViewController:bbp];
passcodeNavigationController.navigationBar.hidden=YES;
[self.navigationController presentModalViewController:passcodeNavigationController animated:YES];
[passcodeNavigationController release];
now from UIViewController_B try to present in UIViewController_C as below code.
UIViewController_C *bbp=[[UIViewController_C alloc]initWithNibName:#"UIViewController_C" bundle:nil];
UINavigationController *passcodeNavigationController = [[UINavigationController alloc] initWithRootViewController:bbp];
passcodeNavigationController.navigationBar.hidden=YES;
[self.navigationController presentModalViewController:passcodeNavigationController animated:YES];
[passcodeNavigationController release];
last and final thing on every back button of view controller write below line of code.
[self dismissModalViewControllerAnimated:YES];
if you want more help than comment bellow.
One Solution:
UIViewControllers presented modally are not necessarily deallocated on -dismissViewController:animated.
This means that by passing a reference to UIViewController_A through _B to _C, you can call -presentViewController:animated and -dismissViewController:animated for the respective UIViewControllers via UIViewController_A.
Code:
1. UIViewController_B
- (void) showUIViewController_C {
[self dismissViewControllerAnimated:TRUE completion:^{
UIViewController_C *controller_C = [[UIViewController_C alloc] init];
controller_C.parentController = self;
[self.parentController controller_C animated:TRUE completion:nil];
}];
}
2. UIViewController_C
- (void) dismissUIViewController_C {
[self dismissViewControllerAnimated:TRUE completion:^{
[self.parentController.parentController presentViewController:self.parentController animated:TRUE completion:nil];
}];
}
Where I am using *parentController as the naming convention for whatever class your previous UIViewController on the stack may be.
It dips back to UIViewController_A briefly because I am calling -dismiss and -present in the completion block, though that actually looks rather fun.
I am presenting a modal view controller on the iPad which changes the presenting view controller while presented. For example:
A view controller VC presents the modal view controller when the user selects a cell in a table view.
The user selects an item on the modal view controller and another VC instance is opened in place of the first. Importantly, the view controller instance replacing the first is of the same type.
The modal view controller cannot be dismissed or an EXC_BAD_ACCESS exception occurs.
The failing dismiss is understandable: the presenting view controller is no longer available. Basically, how would I dismiss this presented modal view controller from a different presenting view controller?
The code I already have is:
ViewController1.m
- (void)showModalViewController:(UIViewController *)viewController
{
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
viewController.navigationItem.rightBarButton = [[UIBarButton alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissModalViewController)];
[self presentViewController:navigationController animated:YES completion:nil];
}
- (void)dismissModalViewController
{
[self dismissViewControllerAnimated:YES completion:nil];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
Thanks for your suggestions but I solved the issue by using delegation. The presented view controller defined a delegate to notify the presenter when an action occurred.
ChildViewControler.h:
#protocol ChildViewControllerDelegate <NSObject>
- (void) childView:(ChildViewController *)childView didSelectItem:(Item *)item;
#end
ChildViewController.m:
// in interface
#property (nonatomic, weak) id <ChildViewControllerDelegate> delegate;
// in implementation
- (void)closeView:(Item *)anItem
{
[self.delegate childView:self didSelectItem:anItem];
}
ViewController1.m:
- (void)showModalViewController:(UIViewController *)viewController
{
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
viewController.navigationItem.rightBarButton = [[UIBarButton alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissModalViewController)];
// Different view controller types may be passed here so check is required...
if (viewController.class == [ChildViewController class]) {
((ChildViewController *)viewController).delegate = self;
[self presentViewController:navigationController animated:YES completion:nil];
}
- (void)childView:(ChildViewController *)childView didSelectItem:(Item *)item
{
[self dismissViewControllerAnimated:YES completion:nil];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
// Perform action required with 'item'
}
You can try to change
self.presentingViewController
(The view controller that presented this view controller or its farthest ancestor.)
property in your modal View Controller before dismissing.
Here is your problem:
//...
viewController.navigationItem.rightBarButton = [[UIBarButton alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissModalViewController)];
//...
- (void)dismissModalViewController
{
[self dismissViewControllerAnimated:YES completion:nil];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
You try to dismiss presenter view controller (that is currently seems to be swithced to another already) instead of presented modal view controller (in your case it UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];)
so (if presenters view controllers not the tabs of tabViewController or not stored in navigationController's stack or somewhere else) you must to store reference to it somewhere else than in presenter view controller which will be switched and could be deallocated.
As per document presentingViewController is a readonly property.
You could not modify it.
#property(nonatomic,readonly) UIViewController *presentingViewController NS_AVAILABLE_IOS(5_0);
I have not tested this code but there may be error in dismissModalViewController.
please put break point on this method the first line is perfect may your second line may cause error,may self.tableView is not accessible or self.tableView indexPathForSelectedRow may be nil.
- (void)dismissModalViewController
{
[self dismissViewControllerAnimated:YES completion:nil];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
Thanks.
I use the following code to switch from main view (ViewController.h)to another view (TouchViewController) and then similarly switch from this view to the next view (QuestionsViewController).
How do I go back to the main view from here? i.e what is the value for initWithNibName for MainStoryboard.storyboard?
- (IBAction)startGame:(UIButton *)sender {
TouchViewController *second = [[TouchViewController alloc] initWithNibName:#"TouchViewController" bundle:nil];
[self presentViewController:second animated:YES completion:nil];
}
and
- (IBAction)selectTopic:(UIButton *)sender {
NSString *category = [[sender titleLabel] text];
[[NSUserDefaults standardUserDefaults] setObject:category forKey:#"MyKey"];
QuestionsViewController *second = [[QuestionsViewController alloc] initWithNibName:#"QuestionsViewController" bundle:nil];
[self presentViewController:second animated:YES completion:nil];
}
Now to go back to the main view i have a problem i.e
- (IBAction)backtoMainMenu:(id)sender {
ViewController *second = [[ViewController alloc]initWithNibName:#"MainStoryboard.storyboard" bundle:nil];
[self presentViewController:second animated:YES completion:nil];
}
At this point the app just crashes! I suppose it's because initWithNibName:#"MainStoryboard.storyboard" can't be used but the ViewController does not have any other xib file - so what do I do?
First of all storyboard can't be used as a xib file they are not the same thing. Second you have one view controller that presents a view controller that also present a view controller so what you can do is something like this from the third view controller:
[self.presentingViewController.presentingViewController
void)dismissViewControllerAnimated:YES completion:nil];
This is the fastest way you can do it, but it's not really reusable, if you have a more complex view controller hierarchy than it will be really ugly to do it like this, other solutions can be delgates or notifications.
Also just a piece of advice, when you want to go back to a previous screen in general you don't what to create a new view controller (as you try in backToMainMenu method, you simply want to remove all the view controllers (screens) until you reach the desired screen.
I am using a class inherited from UINavigationController present as a modal view, in the navigation bar I have a button 'Done' which will dismiss the modal view when user tap on it. Everything behave normal except the dealloc() in ImagePickerController, GroupPickerController (which is initialized as root view) not get called when I dismiss the modal view. This cause the leak of the memory.
Here is the code use it:
ImagePickerController *picker = [[ImagePickerController alloc] initWithRootViewController:nil];
// don't show animation since this may cause the screen flash with white background.
[self presentModalViewController:picker animated:NO];
picker.navigationBar.barStyle = UIBarStyleBlack;
[picker release];
Here is what's inside ImagePickerController, which is a UINavigationController:
- (id)initWithRootViewController:(UIViewController *)root {
GroupPickerController *controller = [[GroupPickerController alloc] initWithNibName:nil bundle:nil];
self = [super initWithRootViewController:controller];
[controller release];
if (self) {
self.modalPresentationStyle = UIModalPresentationPageSheet;
}
return self;
}
- (void)dealloc {
[super dealloc];
}
-(void) dismiss
{
[self.navigationController setViewControllers:nil];
[self dismissModalViewControllerAnimated:YES];
}
Here is the code in GroupPickerController, it response to a button in the navigation bar, to dismiss the modal view:
...
#pragma mark - button actions
- (void)done {
[self.parent dismiss];
}
I tried to manually remove the views from NavigationController, seemed not no effect at all...
[self.navigationController setViewControllers:nil];
Thanks for the help!
UPDATED:
Please disregard this question, apparently it's a mistake. :(
Finally get the problem solved... not change any of the code, but a rebuild the project. :(
First of all, you should not be subclassing UINavigationController:
This class is not intended for subclassing.
What does this line do?
controller.parent = self;
If the controller retains the parent-property, you have a retain cycle which would cause the issue you are describing. Remember that all view controllers in the UINavigationController stack can access the navigation controller with the -navigationController property.
There's is a difference between a UIViewController begin dismissed and released.
When you dismiss it, it can be released at any moment, but not necessarily immediately.
Are you sure you have a memory leak ? Maybe the picker is released a few seconds after the dimiss.
If you really have a memory leak, that means there is another place where you picker is retained.