I am working on an iPad app that has a login controller which segues into a view controller. The login controller is declared thusly:
#interface LoginController : UIViewController <UITextFieldDelegate>
and the storyboard has (as expected), a username and password text field and a button for authentication and logging into the main app. The button calls the shouldPerformSegueWithIdentifier(...) function and the view switches from the login view to the main view.
I would like to also mimic this programmatically, when the user hits return on the password text box. I've trapped the event, but I can't seem to get the switchover to happen. The code I'm using is:
if (theTextField == self.password)
{
BOOL loginSuccessful = [self shouldPerformSegueWithIdentifier:#"switchToViewer" sender:self];
if (loginSuccessful == YES)
{
[self dismissViewControllerAnimated:YES completion:^{
NSLog(#"I should be dismissing here!\n");
}];
}
}
However, the view never gets dismissed. I should note that this is on iOS 7, I don't know if that matters. Any ideas?
EDIT: My workaround for now is to spoof the button touch event:
[self.signIn sendActionsForControlEvents: UIControlEventTouchUpInside];
Hacky, but it works :)
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
Related
I've got a HomeViewController that has different modal segues to several other UIViewControllers. If I try to show the keyboard on a UITextField within the HomeView, everything works fine. However, if I try to show the keyboard on a UITextField (using becomeFirstResponder) after returning from any of the modal View Controllers, the keyboard never shows.
Here's some sample code from one of the setups I've tried:
In HomeViewController:
- (void)viewDidAppear:(BOOL)animated
{
static BOOL firstTimeComplete = false;
if (!firstTimeComplete) {
firstTimeComplete = true;
} else {
UITextField *textField = [[UITextField alloc] init];
[self.view addSubview:textField];
[textField performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:3]
}
}
In ModalViewController:
- (IBAction)done:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
Where done: is linked to the "Done" button via a touch up inside event.
A few things I've tried:
Converting the modal segues to push segues fixes the issue, but I don't want a Nav bar in any of the child views
I've tried disabling and enabling animations when dismissing the
modal view controller (using dismissViewControllerAnimated:)
Using unwind segues in the storyboard rather than doing it programmatically
Anyone have an idea of what may be going on?
After deleting tons of code, I finally found out that a custom NavigationController was being used and this was the root cause:
#implementation MSLNavigationController
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate
{
return NO;
}
#end
The app doesn't need this code, so I've nuked the file. (But an explanation as to why this would be hiding the keyboard would be awesome :))
You did not call [super viewDidAppear:animated]
In place like that i have workaround that works pretty well
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
{
if (self.textView.text.isNotEmpty)
{
[self.textView becomeFirstResponder];
}
});
}
I have been struggling with this problem for some time, so I'll post here what I found out.
I was calling textField.becomeFirstResponder() in viewWillAppear but on iOS 7, after the modal was dismissed, the keyboard would not show again, even when you would tap on the textField.
For me calling textField.resignFirstResponder() when the modal is presented, solved the issue. It seems like the input field was already marked as first responder and then would not react to the new calls.
im have two view controller UserListView and UserProfileView!
in UserListView view controller i'm have a button for swtich to UserProfileView and here is code.
UserListView.m - Click Action
- (IBAction)SettingClick:(id)sender
{
UserList *UserProfile = [self.storyboard instantiateViewControllerWithIdentifier:#"UserProfileView"];
[self presentViewController:UserProfile animated:YES completion:nil];
}
And code working fine, when user switch to profile (UserProfileView) have a close button back to UserListView and here is code.
UserProfileView.m - Close click action
- (IBAction)CloseClick:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
UserProfile *UseList = [self.storyboard instantiateViewControllerWithIdentifier:#"UserListView"];
[self presentViewController:UseList animated:YES completion:nil];
}
in this code i will using [self dismissViewControllerAnimated:YES completion:nil]; to close UserProfileView view controller for low ram usage and it work.
But affter i close UserProfileView i want to open this view controller again and it do not work, UserProfileView not showing again??
i using xcode 5 and building an App for ios 7, please help.
Thanks for your time.
If I understand correctly, when you call SettingClick: your app is displaying a UserList. So, when you dismiss a view controller presented on top of it, you should go back to UserList without the need for presenting it again. So you can try with:
- (IBAction)CloseClick:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
This will work unless you had originally presented UserList as well. In this case, UserList will be dismissed with the top controller. In this case, you can delay presenting a second time UserList after dismissing UserProfile, and it should work.
In the latter case, I would suggest you to use a navigation controller instead of simply presenting your controllers like you are doing. As you see, it is not really straightforward and you will get into catches of any kind. Presenting a controller works ok when you present just one controller at a time. On the other hand, if you instantiate a UINavigationController, this will handle the controllers' hierarchy for you.
use this -
- (IBAction)CloseClick:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
This will return to your previous view when u click that button that has been linked with this action
I'm in front of a little problem :
I have something like that when launching the app :
(1) Splash view --> I am logged ? --> (3) HomeView | else (2) Login view
So when I log, then I go to the home view. And when I logout, I can unwind to the login view because i came from it.
BUT if I don't pass from the login view and redirect directly to the home view, I can't unwind to login view when logout.
Someone know a solution about this ?
I just put my logic here:
Take/add one viewController such like, DummyViewController as rootViewController of your app.
in the DummyViewController's viewWillAppear method put logic like a
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(login == YES)
{
//go to home screen
}
else
{
// go to login screen
}
}
In DummyViewController you just need to write code in viewWillAppear not anymore.
If you want to Logout then just call
[self.navigationController popToRootViewControllerAnimated:YES];
And viewWillAppear method of DummyViewController will manage your screen based on login status.
You can use NSNotificationCenter to notify your root class when logout done. Then pop to your rootViewController
If you are using UINavigationController, Just present your login controller on NavigationController's RootViewController as like below
-(void)logoutNotification
{
logout = YES;
}
-(void)viewDidAppear:(BOOL)animated
{
if (logout)
{
AuthController * auth = [[AuthController alloc] init];
[self presentViewController:auth animated:NO completion:^{
}];
logout = NO;
auth = nil;
}
}
Maybe is a bit dummy way to do it, but you can simply ALWAYS load the login view and delegate to it the "I'm logged?" check. You can load it hidden or with a waiting sign or whatever... This way you have it already loaded when you logout.
Without seeing your code I can't show you how to, but I guess the logic is enough.
I have a UIViewController then with this code I present a modal view controller for entering the password.
[self performSegueWithIdentifier:#"SeguePassword" sender:self];
in Storyboard:
Storyboard Segue
Identifier = SeguePassword
Style = Modal
Transition = default
Animates = not checked
When I click "Cancel" on the modal which has the following code:
[self dismissViewControllerAnimated:NO completion:^{}];
Now when I get back the keyboard is hidden. I have code to show a toolbar with a button called hide. And I see that, but not the keyboard.
Does anyone have any ideas or directives they have taken to fix an issue like this? It seems it recently started after converting changes for iOS 7.
My solution:
I found this because after I clicked the but on an alert box a few times out of frustration I saw the keyboard in the wrong orientation. I.e.., a landscape keyboard when the app is portrait only on iPhone.
Before:
- (NSUInteger) supportedInterfaceOrientations {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
return UIInterfaceOrientationLandscapeLeft | UIInterfaceOrientationLandscapeRight;
} else {
**return (UIInterfaceOrientationPortrait);**
}
}
After:
- (NSUInteger) supportedInterfaceOrientations {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
return UIInterfaceOrientationLandscapeLeft | UIInterfaceOrientationLandscapeRight;
} else {
**return (UIInterfaceOrientationMaskPortrait);**
}
}
I changed the Portrait to use the Mask integer and everything started working again.
The keyboard is only visible if some object is the firstResponder. Look at the documentation for the UIResponder class.
If your modal view controller uses the keyboard, which it sounds like it does, it takes first responder status and whatever had it on your original screen loses it. If you want to return to the same state from the modal view controller, with some object having input focus -- i.e. being the firstResponder -- with the keyboard visible you will have to have write code to make that happen. In particular, your original view controller has to know when the modal view controller terminates so that it can make your input object, textField or textView or whatever, be the first responder with the statement:
[objectIWantToBeFirstResponder becomeFirstResponder];
Now how do you know when the modal view controller is done?
There are a couple obvious techniques.
If you want to use a segue then the modal view controller will have to
explicitly let the presenting view controller know that it is
terminating. You will probably want to define a protocol in the
modal view controller and have the presenting view controller adopt
it. When the modal view controller wants to exit it first tells the presenting
view controller.
Since you are manually triggering the segue perhaps
you're willing to present the modal view controller in code. If so then
you can use a completion block:
ModalViewControllerClass *vc = [[ModalViewControllerClass alloc] init];
// whatever else you need to do to the vc
[self presentViewController:vc animated:YESorNO completion:^{
// This code gets executed when the presented view controller exits
[objectIWantToBeFirstResponder becomeFirstResponder];
}];
Let me explain. I have multiple UIViewControllers. On my MainPageController, I have 3 UIViews. Let's enumerate it this way: the first UIView is called LoginView, the second is called HomeView and the other one is called RegView. Now in HomeView, there are multiple buttons that will lead to other UIViewControllers. For example, one button will lead to StoreController. Now if I am inside StoreController and I want to go back to MainPageController, I simply call:
[self dismissModalViewControllerAnimated:YES completion:nil]
This will send me back to the HomeView.
That is good. However, inside the StoreController, there are buttons which will supposedly direct me to LoginView or RegView, whichever button was tapped. The problem is when the method [self dismissModalViewControllerAnimated:YES completion:nil], it only take me back to HomeView, no matter which button I pressed.
So how will I display the right UIView once the dismissModalViewControllerAnimated is called?
EDIT:
This is how I show the UIViews:
-(void)viewDidLoad
{
//Initialize the views here...
}
-(void)showViewByTag:(NSInteger)tag
{
if (tag == 1)
{
[self.view addSubview:loginView];
}
else if (tag == 2)
{
[self.view addSubview:homeView];
}
else
{
[self.view addSubview:regView];
}
}
Now I call the method showViewByTag: somewhere in my code to display the views.
What you could try and do is following: before calling [self dismissModalViewControllerAnimated:YES completion:nil] (and thus go back to your home view), change the view currently displayed in your MainPageController:
[(MainPageController*)self.presentingViewController showViewByTag:desiredViewTag];
[self dismissModalViewControllerAnimated:YES...];
If you are worried at the cast and you foresee that self.presentingViewController might be not of MainPageController type on some occasions, then you can check explicitly for its type:
if ([self.presentingViewController isKindOf:[MainPageController class]])
[(MainPageController*)self.presentingViewController showViewByTag:desiredViewTag];
[self dismissModalViewControllerAnimated:YES...];
For this to compile, MainPageController.h must be imported in your modal controller class.
dismissModalViewController will always bring back the viewController which presented it ,and that can be only one,so the ideal way would be to tell the navigationController to initWith your desired viewController..
eg on regButton click in the presented modalview
RegViewController *regViewController = [[RegViewController alloc]initWithNibNam:#"RegViewController" bundle:nil];
[self.navigationController initWithRootViewController:regViewController];