Modal view controller won't dismiss itself - iphone

What I'm doing:
In my app, I'm presenting a modal view controller (containing app settings) using the following code:
optionsViewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentModalViewController:optionsViewController animated:YES];
This transition just curls up the bottom part of the view to expose a few settings. (See the 'Maps' app for an example.) When you tap on the top half of the page, where the original view is still there but grayed out, the modal view controller is automatically dismissed (handled by the OS, I didn't code for this).
-
What's not working:
This is working fine in iOS 4 (my app is currently on the App Store in fact). But in iOS 5, it looks like Apple have changed the behavior of this transition, and the view controller no longer dismisses itself. I'm trying to replicate the behavior that was handled by the OS before, but can't figure out how to.
-
What I've tried:
Adding an invisible button to the top of the options view doesn't work. The page then curls up the full way, which I don't want.
Apart from this, I'm stuck. How should I replicate how this worked originally (or was I doing it the wrong way from the start!). Any help is much appreciated!

Dude, I ran into the same problem.. and here is what I found about using parentViewController:
Note that as of 5.0 this no longer
will return the presenting view
controller.
This was written in the header file of UIViewController...
I am using ShareKit, and the modalViewController was working perfectly in iOS4, but in iOS5, it just won't dismiss itself! This is because in their code, they are using:
[[currentView parentViewController] dismissModalViewControllerAnimated:animated];
and parentViewController will return nil, since this is a modal presented view controller...
By searching for a solution, I found your question.. So, I decided to fix it myself :P
I changed the previous line to this:
[currentView dismissModalViewControllerAnimated:YES];
Works like a charm.
EDIT: Depending on how you interpret the original question, there are two answers. Here's the second:
In iOS5 it seems that the modal controller only dismisses itself when you click the curl, but not above the curl or the backgound. In iOS5, in order to actually get the modal view to dismiss itself when tapping the background or above the curl I added the following code to the controller, to listen to taps on the modal view, but ignore taps to buttons. This should mimic the behavior in previous version of iOS when working with a modal controller with page curl.
- (void)viewDidLoad
{
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
tap.delegate = self;
[backgroundView addGestureRecognizer:tap];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
//change it to your condition
if ([touch.view isKindOfClass:[UIButton class]]) {
return NO;
}
return YES;
}
- (void)handleTap:(UITapGestureRecognizer *)sender {
[self dismissModalViewControllerAnimated:YES];
}

What's the code you're using to dismiss the modal view controller? I've seen code like this:
[self.parentViewController dismissModalViewControllerAnimated: YES];
that doesn't work on all versions of the OS. However, this:
[self dismissModalViewControllerAnimated: YES];
should.

I had the same problem, also affects those who use:
[self.parentViewController.parentViewController dismissModalViewControllerAnimated:YES];
I fix it with a Observer, adding this where you had the dismiss:
[[NSNotificationCenter defaultCenter] postNotificationName:#"yourObserverName" object:self];
And this in the parent parent view controller:
// add in viewDidLoad for example
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissModalVCFromParent:) name:#"yourObserverName" object: nil];
//The function
- (void) dismissModalVCFromParent:(NSNotification *)notif
{
[self dismissModalViewControllerAnimated:YES];
}
// Don't forget remove
[[NSNotificationCenter defaultCenter] removeObserver:self];

This seems to work on the (now final version of) ios 5.
I notice that you have to tap is a specific region to dismiss the page curl - tapping near the edges of the top portion of the screen does not seem to do anything, but the center, blurred section above the page curl graphic consistently results in dismissing the modal view.
I'm not sure whether that narrow tap region behavior is new to ios 5 or already existed and I never noticed before. Hopefully that is helpful!

In iOS5 it seems that the modal controller only dismisses itself when you click the curl, but not above the curl or the backgound. In iOS5, in order to actually get the modal view to dismiss itself when tapping the background or above the curl I added the following code to the controller, to listen to taps on the modal view, but ignore taps to buttons. This should mimic the behavior in previous version of iOS when working with a modal controller with page curl.
- (void)viewDidLoad
{
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
tap.delegate = self;
[backgroundView addGestureRecognizer:tap];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
//change it to your condition
if ([touch.view isKindOfClass:[UIButton class]]) {
return NO;
}
return YES;
}
- (void)handleTap:(UITapGestureRecognizer *)sender {
[self dismissModalViewControllerAnimated:YES];
}

Thanks guys, this saved me a lot of time. I just noticed that the presentModalViewController and dismissModalViewController methods are deprecated according to the source code for UIViewControoler.h. There are alternative presentViewController and dismissViewController methods.

Related

Attempt to dismiss from view controller while a presentation or dismiss is in progress

I have TWO UIViewController classes, where in FirstClass i have an UIButton for Login, when user taps on button, i will display SecondClass... For that i have done,
SecondClass *index = [[SecondClass alloc] init];
[self presentModalViewController:index animated:YES];
In SecondClass i have a logout button, which will redirect to FirstClass, for that i have done,
[self dismissModalViewControllerAnimated:YES];
When i press Logout button in SecondClass, i get the warning msg
**Attempt to dismiss from view controller <FirstClass: 0e39w88e160> while a presentation or dismiss is in progress!**
What is the problem here..
Added both iOS 6 and pre-iOS 6 answers:
iOS 5.0 and later
When you logout, add this check before dismissing:
if (![self.presentedViewController isBeingDismissed])
{
[self dismissModalViewControllerAnimated:YES completion:nil];
}
iOS 4.X and less
Add this check before dismissing:
if (![[self modalViewController] isBeingDismissed])
{
[self dismissModalViewControllerAnimated:YES];
}
Call these lines where you logout & then check:
if (![[self modalViewController] isBeingDismissed])
{
[self dismissModalViewControllerAnimated:YES];
}
There are many things that may cause this, here are some options:
You forgot to call super on one of the ViewController methods such as viewWillAppear, viewWillAppear etc. Consult the UIViewController documentation to see when you have to call super.
The dismissModalViewControllerAnimated: method is being called more than once, this can happen if you added a target to the UIButton more than once.
To get better understanding of the problem please paste the code of both view controllers in its entirety.

touchesBegan to dismiss a view

I have a ViewController with a full screen UIImageView. I've hidden statusBar and NavigationBar, so there is no way to go back but tapping somewhere.
So i was thinking to go back just using this touchesBegan
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self dismissModalViewControllerAnimated:YES];
}
I also tried to use this way
-(void)viewDidLoad
{
[super viewDidLoad];
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[self.navigationController setNavigationBarHidden:YES];
UITapGestureRecognizer *tapRecognizer;
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissModalViewControllerAnimated:)];
[imageFrame addGestureRecognizer:tapRecognizer];
imageFrame.image = [UIImage imageWithContentsOfFile:image];
[tapRecognizer release];
}
I got stuck there, with this full screen image and i can't go back.
How can I dismiss the viewcontroller?
If you are using a navigation bar I'm assuming that you did a pushViewController to start this view controller? In which case you would want
[self.navigationController popViewControllerAnimated:YES];
to dismiss the controller.
Of course your solution assumes that the image view is in fact a modal view controller presented with presentModalViewController:animated: somewhere.
My suggestion would be to have a UIButton placed on top of your image view and sized the same. Hook up its Touch Up Inside to your go-back method and then set its alpha to 0 or Hide it through IB or properties. The button won't be displayed but it'll still receive touch events.
You should call dismissModalViewControllerAnimated: on the parent view controller, and not on the one you wanna hide.
Also make sure to set the UIImageView's userInteractionEnabled property to yes, because unlike regular UIView's it doesn't answer touch events by default.

Modal View Controllers appear behind Action Sheet

My modal view controllers are being shown behind my UIActionSheet, and my UIActionSheet is not getting dismissed. I am using:
[self presentModalViewController:composeTweetView animated:YES];
To present my modal view controller.
My action sheet is being shown from the tabBar:
[actionSheet showFromTabBar:self.parentViewController.tabBarController.tabBar];
This code worked on iOS 4.0.
if (buttonIndex == 0) {
if (self.isLoggedIn) {
[FlurryAPI logEvent:#"ST_REPLY_CLICKED"];
composeTweetView.isDirectMessage = FALSE;
[self presentModalViewController:composeTweetView animated:YES];
[composeTweetView release];
}
else {
LoginViewController* loginView = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
loginView.delegate = self;
loginView.isPostingComment = TRUE;
self.isReply = TRUE;
[self presentModalViewController:loginView animated:YES];
[loginView release];
[composeTweetView release];
}
}
Summary:
I have a UIViewController that contains a UITabBar. I am presenting a UIActionSheet which has a few buttons that present a modal view controller. When the modal view controller is presented, the UIActionSheet should dismiss itself and the modal view should be on the top of the stack. The problem is, the UIActionSheet does not dismiss, and the modal view is loaded behind it. This problem did not occur up until iOS 4.2.1
Steps to Reproduce:
Create a TabBar project, setting
your Base SDK to iOS 4.2.1
Create a button or trigger to show a UIActionSheet
Allow one of the buttons in the UIActionSheet to present a modal view controller using the syntax: [actionSheet showFromTabBar:self.parentViewController.tabBarController.tabBar];
Expected Results:
1. The UIActionSheet should dismiss itself, and the modal view should appear in front
Actual Results:
1. The UIActionSheet does not get dismissed and the modal view appears behind it.
Regression:
This problem was not apparent prior to iOS 4.2.1
Notes:
I have tried other ways of displaying the UIActionSheet all of which don't work as intended:
//[actionSheet showInView:self.parentViewController.tabBarController.tabBar];
//[actionSheet showInView:[self.view window]];
//[actionSheet showInView:self.parentViewController.tabBarController.view];
//[actionSheet showInView:[UIApplication sharedApplication].keyWindow];
In putting together a sample in order to reproduce your problem I took a look at the UIActionSheet delegate methods. I believe you can use:
actionSheet:didDismissWithButtonIndex:
instead of
actionSheet:clickedButtonAtIndex:
I haven't tested it, but I believe you still get the same button number and it doesn't fire until the actionsheet has disappeared from the view.
I don't understand your problem sorry but everything works great for me. I have tried the following code in Simulator and on 4.2.1 iPod Touch 4G (both worked)
- (IBAction)doit {
UIActionSheet *actionSheet = [[UIActionSheet alloc]initWithTitle:#"title" delegate:self cancelButtonTitle:#"not OK" destructiveButtonTitle:#"Absolutely" otherButtonTitles:nil];
[actionSheet showFromTabBar:self.tabBarController.tabBar];
[actionSheet release];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
UIView *v = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 320, 480)];
v.backgroundColor = [UIColor redColor];
[self.view addSubview:v];
[v release];
}
}
created the TabBar sample project
added button to firstView-nib and connected with the appropriate IBAction (have to name the FileOwner to FirstViewController)
set the delegate method in FirstViewController.h (<UIActionSheetDelegate>)
added the code above
//EDIT: ok I saw that you want to present it modally but even this works for me on 4.2.1
TMPController *tmp = [[TMPController alloc]initWithNibName:#"TMP" bundle:nil];
[self presentModalViewController:tmp animated:YES];
[tmp release];
maybe it works because I use self.tabBarController.tabBar, try that
Not sure if you have already resolved this. But I didn't see a clear answer above. I was having exactly the same problem as you. So in the end, it was an issue on my side, but what help me debug this was to add the actual action code in the method
actionSheet:didDismissWithButtonIndex:
as suggested by Matthew.
This proved that the view was actually dismissed. That's when I realized that I put the UIActionSheet alloc in my viewWillAppear method. So each time the view appears it re-creates the action sheet.
This seems weird as the SDK Documentation states:
actionSheet:clickedButtonAtIndex:
…
The receiver is automatically dismissed after this method is invoked.
I can think of 3 possibilities why it may not disappear:
The main runloop (main thread!) which handles the animations and display stuff is not called. Do you work something heavy in your main thread like synchronous networking calls? (note the word "after" in the SDK text)
Somehow you schedule to show the action sheet multiple times
You display the same view controller instance modally that is already somewhere below the current view on the view stack.
I just wrote a quick sample app to test this out and it worked just as I expected (i.e., I couldn't repro your issue). It was a little different in that I didn't do a TabBar application but I'm still hopeful this helps. What I did with the UIActionSheet was to show it like this: [actionSheet showInView:self.view]. Maybe that will work?
Sorry for the rushed answer, I was on my way out when this caught my eye. :)
I have also faced problems like this with iOS 4.2.
I think you should try with the following steps:
Create a separate method for the code you want to be executed on clicking the actionsheet button. suppose the method is -(void)presentModalView{}
2.Now in the - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex method,Call the presentModalView{} method for the clicked button index like this:
if(buttonIndex==0)
{
[self performSelector:#selector(presentModalView) withObject:nil afterDelay:0.3];
}
3.You can also try by different delay time.
Hope it helps..
I had the same issue calling MailComposeView from AlertBox/ActionSheet caused the MailCompseView to come behind invisible screen.
I solved it by calling dismissWithClickedButtonIndex:animated: in the actionSheet button handler.
The problem has been resolved in iOS5.

iPad/iOS modalView jumps left on dismiss

I added a modalView to my App, everything working fine, but on closing the modal, the whole modalView jumps about 1-2 centimeters to left while it disappears.
I did not find any reason for it yet, so here is the code regarding modal:
AppController:
- (void) showNameModal:(Player *)player
{
namesModal = [[PlayerModalView alloc] init];
namesModal.delegate = self;
namesModal.player = player;
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:namesModal];
navCon.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:navCon animated:YES];
[navCon release];
[namesModal release];
}
- (void)didDismissModalView
{
[self dismissModalViewControllerAnimated:YES];
}
ModalView:
- (void)dismissView:(id)sender
{
[delegate didDismissModalView];
}
called via navigation buttons as well ass via keyboard by
[self dismissView:nil];
As you can see, there is nothing special in it, could be taken from a manual actually.
What happens in detail:
Modal appears in center of screen, slides in from the bottom. centered all time.
i can handle some actions in the modalView, it stays centered.
now, dismissing the view makes it jumping to the left, than slides out.
Since it's a forced landscape-right app (currently), I was only able to notify the left-jump.
Any ideas how to get this jumping away?
Thanks
Try this,
- (void)didmissView:(id)sender
{
[self.navigationController didmissModelViewControllerAnimated:YES];
}
You are not modally presenting an instance of PlayerModalView but rather a UINavigationController. The left jerk you see is most likely the default animation of the navigation controller attempting a slide transform to the (non-existant) previous view.
It doesn't sound like you need a navigation controller for the PlayerModalView. Instead, you should create an ordinary view controller for it.
This solution seems to work well: Modal View Controller with keyboard on landscape iPad changes location when dismissed
To simplify resigning the first responder (if finding it is difficult), you can just call
[self.view endEditing:YES];
[self dismissModalViewControllerAnimated:YES];
The problem is that the UIViewController you're showing modally doesn't allow the orientation you're presenting it in, so when it disappears, it will do that in a direction that it considers "allowed".
Add this to the UIViewController for you modal view:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}

How to make a iPhone keyboard not visible as it rises?

When my iPhone app starts up, the main screen has a keyboard. Currently the keyboard rises as soon as the rest of the interface is displayed and this is visually distracting.
How can I have the view display with the keyboard already up?
Since I am already faking some of the rest of the screen during startup so that the user sees what they last were doing, I thought that I could fake the keyboard as well. But if the motion is there when the real keyboard appears, I've lost the effect. The keyboard is, as far as I know, on a separate window, not just a separate view, so I can't overlay it with my own image.
Is there a way to either overlay the keyboard withy my own image as it appears, or not show the keyboard until it is all the way up, or make its animation instant?
My original answer has the keyboard animate in along with the view controller if it's an animated transition (i.e. pushing a view controller or presenting a modal controller with animated: YES). However, the keyboard still animates in if the new view controller is displayed without an animated transition, so it doesn't solve your problem.
Here's another approach that worked in my testing. Try disabling animations while you're displaying the controller + keyboard.
[UIWindow beginAnimations: nil context: NULL];
[UIWindow setAnimationsEnabled: NO];
RestoredController *controller = [[[RestoredController alloc] init] autorelease];
[self.navigationController pushViewController: controller animated: NO];
[UIWindow commitAnimations];
You'll still need to make field the first responder in viewWillAppear: or viewDidAppear:
In viewWillAppear:, ensure the view is loaded (via self.view) and set the first responder to the correct field. This will display the keyboard fully when the view is actually displayed instead of animating it in.
For example:
- (void) viewWillAppear: (BOOL) animated {
[super viewWillAppear:animated];
NSString *storedID = [[NSUserDefaults standardUserDefaults] stringForKey:#"storedID"];
if ([storedID length] > 0) {
idField.text = storedEmail;
[passwordField becomeFirstResponder];
} else {
[idField becomeFirstResponder];
}
}
Not an answer, but here is some code.
I set the font for my UITextView in viewDidLoad. The view is instantiated by the NIB and the outlet is correctly set up.
-(void)viewWillAppear:(BOOL)animated;
{
[super viewWillAppear:animated];
// Start with the text from the currently edited message
NSString *startString = self.messageManager.editingMessage.text;
// start string processing omitted
self.editingMessageEditingView.text = startString;
if([self showingMessageList]) {
[self.editingMessageEditingView resignFirstResponder];
} else {
#if DEFAULT_SCREEN==1
[[self.tools.items objectAtIndex:kPVToolBarItemDelete] setEnabled:NO];
[[self.tools.items objectAtIndex:kPVToolBarItemSendLater] setEnabled:NO];
#else
[self.editingMessageEditingView becomeFirstResponder];
#endif
}