TextField will NOT resign first responder with UIModalPresentationFormSheet view - objective-c

I've created a button on one viewController that loads another view modally using the UIModalPresentationFormSheet presentation style. On this loaded view, I have two textFields, and I'm forcing the first textField to be first responder so that the keyboard will appear immediately with the new view. I've set up the textFields to have an action method that is hooked up to "Did End on Exit" event. However, whenever I hit "return" on the keyboard for either textField, the keyboard fails to go away (Here is my code):
// addCustomPage method that is called when button from original view is touched
- (IBAction) addCustomPage:(id)sender
{
NSLog(#"Adding Custom Page");
if (!self.customPageViewController)
{
self.customPageViewController =
[[CustomPageViewController alloc] initWithNibName:#"CustomPageViewController" bundle: nil];
}
customPageViewController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:customPageViewController animated:YES];
// force keyboard to appear with loaded page on the first textField
[customPageViewController.firstTextField becomeFirstResponder];
}
#interface CustomPageViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *firstTextField;
#property (strong, nonatomic) IBOutlet UITextField *secondTextField;
- (IBAction)keyboardEndOnExit:(id)sender; // DID END ON EXIT EVENT
#end
//in CustomPageViewController.m
-(IBAction)keyboardEndOnExit:(id)sender
{
[sender resignFirstResponder];
}
This is a fairly straight forward problem, and I have no problem normally dismissing keyboards using this technique with basic views and textFields. I'm not sure if using a view in this presentation format, or set up makes things different. Thanks!

You have confirmed that you keyboardEndOnExit method is actually being called?
You could also take a more direct approach by calling [yourTextView resignFirstResponder] when a specific action is take by the user, such as a key pressed etc. I would still check if that method is ever being called using breakpoints or a log.

Have a look at this question. Pretty sure it is the same problem caused by UIModalPresentationFormSheet.

Related

Perform segue from main view on container view

I seem to be unable to understand how to go about this. I have a button on my main view. This view contains a container view. I would like the button on the main view to make the container view segue to another view. I have set up an identifier for the segue, which goes from containerView1 to containerView2. This is a push-segue. The identifier is pushSegue.
On the button on the main view I have tried this:
- (IBAction)btnChangeLocation:(UIButton *)sender {
UIViewController *a = [[ContainerView1 alloc]init];
[a performSegueWithIdentifier:#"pushSegue" sender:nil];
}
I have successfully performed this segue from within containerView1, by just placing within it, and performing the segue from there. It works just fine then.
- (IBAction)testButton:(UIButton *)sender {
[self performSegueWithIdentifier:#"pushSegue" sender:nil];
}
But how would I go if I wanted to trigger the segue on the containerView1, from the button on the main view?
Thanks.
EDIT:
I would also like to be able to perform the same segue, from a container view, that is within the container view.
Just to summarize.
MainView----->ContainerView1-->pushSegue--->ContainerView2
ContaainerView1 has a subContainerView, which also has a button, which causes ContainerView1 to segue into ContainerView2. This button and the button on the MainView does the same thing really, just from different "locations".
EDIT: Added a picture to help explain. http://tinypic.com/r/maxpp2/8
With UIViewController *a = [[ContainerView1 alloc]init]; you are instantiating a new ContainerView1 controller. That won't help you; you need to call performSegueWithIdentifier:sender: on the instance already created.
Depending on how your Storyboard and code are set up, you need to find a way to get a hold of the embedded view controller.
For this set up:
You could do something like this in the main (hosting) view controller:
#property (weak, nonatomic) IBOutlet UIView *childView;
#property (weak,nonatomic) UINavigationController *container;
...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"embedContainer1"]) {
self.container = segue.destinationViewController;
}
}
-(IBAction)doIt:(id)sender {
[self.container.viewControllers[0] performSegueWithIdentifier:#"pushSegue" sender:nil];
}
By implementing prepareForSegue:sender:, you're able to get a reference to the child viewcontroller; cleaner then going through the array of childViewControllers IMHO.

dismissViewControllerAnimated completion block presenter view and modal view flow

I did find an answer to this title and I did do a little research but I'm still not getting the flow. Here is what I want to happen:
1) click a button on the presenter view to open a modal view. 2) retrieve some value and click a button to close the modal view....sending the value to the presentor view and execute a method.
I get that this works like a callback but I still can't figure out where to put the callback stuff.
So, how exactly do I do this? A) In the presentViewController completion block, should I include the presenter view method to execute when modal view is completed?
Or: B) In the modal view's dismissViewControllerAnimated completion block, should I include the presenter view method to execute when modal view is completed?
Can somebody help me with some sample code? Or at least help me get the flow of which block to put the code in?
Thank you, P
You talk about completion blocks so I am assuming you do not want to use delegates.
In the viewController that will be presented modally you need to provide a public completion handler, that will be called when it is dismissed.
#interface PresentedViewController : UIViewController
#property (nonatomic, strong) void (^onCompletion)(id result);
#end
Then in the implementation you need to call this completion block on dismissal. Here I assume the viewController is dismissed on a button click
- (IBAction)done:(id)sender
{
if (self.onCompletion) {
self.onCompletion(self.someRetrievedValue);
}
}
Now back in the viewController that presented the modal you need to provide the actual completion block - normally when you create the viewController
- (IBAction)showModal;
{
PresentedViewController *controller = [[PresentedViewController alloc] init];
controller.onCompletion = ^(id result) {
[self doSomethingWithTheResult:result]
[self dismissViewControllerAnimated:YES completion:nil];
}
[self presentViewController:controller animated:YES completion:nil];
}
This will create the new viewController to be presented modally and define what needs to happen on completion.
You can do this with delegates, that's the way Apple seems to recommend, but that seems like overkill to me. You have a reference to the presenter with the presentingViewController property, so you can just set the value of a property in the presenter from the presented controller in the button click method:
self.presentingViewController.someProp = self.theValueToPass;
[self dismissViewControllerAnimated:YES];
Using delegates is a good way to handle this:
In your PresentedViewController.h
#protocol PresentedViewControllerDelegate <NSObject>
-(void) viewWillDismiss;
#end
#property (nonatomic, weak) id <PresentedViewController> delegate;
Then in your PresentingViewController.h, you would subscribe to this delegate
#interface PresentingViewController : UIViewController <PresentedViewControllerDelegate>
in the .m you must implement the delegate method
- (void) viewWillDismiss {
}
and before you present the view controller set the delegate property you made as self.
presentingViewController.delegate = self;
Obviously not every implementation detail has been done here, but this should get you started.

UIVIew endEditing:YES doesnt hide the keyboard

I have a UIVIew which is a subview and it contains several UITextFields. One of these textfields (which is for DATE) should not be editable using the keyboard, instead of this I use a PopOver with a Datepicker inside.
I run a method when the UIControlEventEditingDidBegin is reached. This method calls the resignFirstResponder on the DateTextField.
Everything works fine if the DateTextField is the first field to edit, but when another textField is edited and of course shows the keyboard and then try to edit the DateField, the keyboard doesn't hide and everything goes normal but with the Keyboard doing anything.
I have tried to call the method endEditing:YES before the resignFirstResponder but it doesn't work. I have tried to run the endEditing:YES and resignFirstResponder on the didEndEditing text field method but theres no way to get that keyboard out.
here is my method:
- (void)showDatePopOver:(id)sender{
[self.view endEditing:YES];
UITextField *textField = (UITextField *)sender;
[sender resignFirstResponder]; // hide keyboard
/** POP OVER LINES**/
}
You should use the textFieldShouldBeginEditing: delegate method instead of resigning first responder in didBeginEditing:
This will allow editing on ALL BUT the dateTextField text field:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
return (![textField isEqual:dateTextField]);
}
You should specify that your view controller is a text view delegate as well like so (in the interface declaration [.h file]):
#interface MyViewController : UIViewController <UITextFieldDelegate>

In Xcode, make a button look like a text field or a textfield look like a button

In Xcode (iOS), I have a screen with a combination of buttons and text fields. However, they look different. Is there a way that I can have a button look like a textfield? Or is there a way I can make a textfield act like a button (perform some action when pressed and don't bring up the keyboard)?
Thanks
You can try one of these:
place a transparent (custom with no image) button on top of the text field and attach the desired action to it
show your view in the implementation of the textFieldShouldBeginEditing: UITextFieldDelegate protocol method
Subclassing is also an option, but it will probably complicate your task too much.
If You want to perform an action and do not bring up the keyboard when pressed on UITextField You can do it like this:
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
// do here everything you want
NSLog(#"Pressed on TextField!");
[self.view endEditing:YES]; // Hide keyboard
return NO;
}
This function will be called every time when pressed on textField.
** Don't forget to delegate UITextField.
Note: With this You will face the problem: if keyboard is shown it will not hide, so You need also add -endEditing:YES.
You need to custom one of them ... or both
Exemple : create a class CustomTextField : UITextField
in this class use :
#import <QuartzCore/QuartzCore.h>
Play with style
Late answer, but nevertheless. I did this to make a UIButton look like a UITextField
#property (weak, nonatomic) IBOutlet UIButton *resolutionButton;
And in viewDidLoad:
dispatch_async(dispatch_get_main_queue(), ^{
[[self.resolutionButton layer] setBorderColor: [UIColor colorWithWhite:0.6f alpha:0.6f].CGColor];
self.resolutionButton.layer.cornerRadius = 5.0f;
self.resolutionButton.layer.borderWidth = 0.5f;
}

UIViewController parentViewController access properties

I know this question has been asked several times and I did read existing posts on this topic but I still need help.
I have 2 UIViewControllers - parent and child. I display the child UIViewController using the presentModalViewController as below:
ChildController *child =
[[ChildController alloc] initWithNibName:#"ChildView" bundle:nil];
[self presentModalViewController:child animated:YES];
[child release];
The child view has a UIPickerView. When user selects an item from UIPickerView and clicks done, I have to dismiss the modal view and display the selected item on a UITextField in the parent view.
In child's button click delegate, I do the following:
ParentController *parent =
(ParentController *)[self.navigationController parentViewController];
[parent.myTextField setText:selectedText];
[self dismissModalViewControllerAnimated:YES];
Everything works without errors. But I don't know how to load the parent view so that it displays the updated UITextField.
I tried
[parent reloadInputViews];
doesn' work. Please help.
Delegation is the way to go. I know some people that may be looking for an easier solution but trust me I have tried others and nothing works better than delegation. So anyone having the same problem, go read up on delegation and follow it step by step.
In your subviewcontroller.h - declare a protocol and declare delegate mthods in it.
#protocol myDelegate
-(void)clickedButton:(subviewcontroller *)subController;
#end
In your subviewcontroller.h, within #interface:
id<myDelegate> delegate;
#property (nonatomic, assign) id<myDelegate> delegate;
NSString *data;
-(NSString *)getData;
In your subviewcontroller.m, synthesize myDelegate. Add the following code to where you want to notify your parentviewcontroller that the subview is done doing whatever it is supposed to do:
[delegate clickedButton:self];
and then handle getData to return whatever data you want to send to your parentviewcontroller
In your parentviewcontroller.h, import subviewcontroller.h and use it's delegate
#import "subviewcontroller.h"
#interface parentviewcontroller : VUIViewController <myDelegate>
{}
In your parentviewcontroller.m, implement the delegate method
- (void)clickedButton:(subviewcontroller *)subcontroller
{
NSString *myData = [subcontroller getData];
[self dimissModalViewControllerAnimated:YES];
[self reloadInputViews];
}
Don't forget memory management!
If a low-memory warning comes in during your modal view's display, the parent's view will be unloaded. Then parent.myTextField is no longer referring to the right text field until the view is reloaded. You can force a reload of the view just by calling parent.view;
However, a better idea might be to have the parent view have a String property that can be set by the child view. Then, when the parent view reappears, put that data into the text field, inside viewWillAppear: for example. You'd want to have the value set to some default value for when the parent view initially shows up too.
-(void) viewWillAppear:(BOOL) animated doesn't get called for me either, exactly when it's a modal view controller. No idea why. Not incorrectly overridden anywhere in this app, and the same problem occurs on the other 2 apps I'm working on. I really don't think it works.
I've used the delegate approach before, but I think that following approach is pretty good as well.
I work around this by adding a private category to UIViewController, like so:
.h file:
#interface UIViewController(Extras)
// returns true if this view was presented via presentModalViewController:animated:, false otherwise.
#property(readonly) BOOL isModal;
// Just like the regular dismissModalViewController, but actually calls viewWillAppear: on the parent, which hasn't been working for me, ever, for modal dialogs.
- (void)dismissModal: (BOOL) animated;
#end
and .m file:
#implementation UIView(Extras)
-(BOOL) isModal
{
return self == self.parentViewController.modalViewController;
}
- (void)dismissModal: (BOOL) animated
{
[self.parentViewController viewWillAppear: animated];
[self dismissModalViewControllerAnimated: animated];
}
#end
which I can now call like this when I want to dismiss the dialog box:
// If presented as a modal view, dismiss yourself.
if(self.isModal)
[self dismissModal: YES];
and now viewWillAppear is correctly called.
And yes, I'm donating a bonus 'isModal' property, so that the modal view can tell how it was being presented, and dismiss itself appropriately.