custom uiview resigned from First Responder - iphone

I checked the documentation related to First Responder but couldn't find some sort of callback which is received when some UIView resigns from being the first responder. Let me explain my scenario.
So, I have a custom UIView (created programmatically) which becomes the first responder when invoked. Now I want to dismiss/remove this view when the user clicks on the superview or some other view. I have written the logic in super view to make it as first responder when it receives a touch event but couldn't find any callback for that custom view to dismiss it when this happens.
Taking another view point I want to get a similar behavior as of the textfield's keyboard, which is dismissed when some other view becomes the first responder.
I hope my questions context is clear.

Since UIView inherits from UIResponder, you can just override the resignFirstResponder method to update your appearance as necessary:
- (BOOL)resignFirstResponder {
if (![super resignFirstResponder])
return NO;
// dismiss self here
return YES;
}

I assume you have implemented all the other view's IBAction methods when being touched. So why not in those methods let this custom view resign first responder or even be released if you find this custom view is not nil and is not needed anymore?

Related

UITextField becomes first responder when view becomes visible, but I don't want it to

This should be simple, but it's becoming a headache. I have a view with a UITextField where the user types some text and clicks search. This pops up another view controller, and I resign first responder on the text field before the first view disappears. When the second view gets dismissed, the first view automatically makes the text field become first responder again and I can't find a way to suppress this. Does anyone have any idea how I can keep the keyboard from popping up when I dismiss the second view?
I tried to resignFirstResponder in viewWillAppear, no effect. I tried the same in viewDidAppear, but the keyboard pops up and then immediately dismisses which is awkward looking.
I appreciate any help.
I found a fix, but its not a very good one.
Found that canBecomeResponder message was being passed to the textfield right after viewWillAppear and before viewDidAppear.
So I set a BOOL value in viewDidAppear to YES, and used that value in textField:shouldBeginEditing method. Basically if the viewDidAppear was not called yet, textField:shouldBeginEditing was returning NO.
I had a similar issue - a UITextView in View Controller A was the first responder. When tapping another UI element on the screen I pushed View Controller B onto the nav stack, but when the back button was pushed and View Controller A was shown again, the UITextView was still first responder.
I solved the issue by calling resignFirstResponder on the text view in View Controller A's viewWillDisappear. Note that this does not work if you call it in viewDidDisappear, since at that point the text view is no longer first responder. It's important to make sure that the text view is actually the first responder when you call resignFirstResponder, otherwise resigning has no effect. You can check this for debugging purposes by inspecting isFirstResponder on the text view.
Please add those Line in your code in my case "tfCode1"
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async {
_ = self.tfCode1.becomeFirstResponder()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
_ = self.tfCode1.resignFirstResponder()
}

UIPopoverController on UITableViewCell

I have a UITableView cell with several UITextFields in it. When a user clicks into one of the textFields, a popover appears with some information. At first, they then had to click outside of either the textField or the popover to clear the popover before then clicking into the next textField. I therefore then added the cell's contentView to the popover controller's passThroughViews property so they can click through the different textFields in that cell at will without having to dismiss the popover controller first. However, it keeps the original popover open (which) is fine, and then opens another identical popover as well.
Is there a way I can tell if a popover is already open before sending the command to open another? I can't think of how to detect this?
If each cell controls the logic of the popover, you need to say to your custom cell to implement UIPopoverControllerDelegate protocol, set the delegate for the popover as self (the cell) and override popoverControllerDidDismissPopover like the following:
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
[self.pop dismissPopoverAnimated:YES]; // hide the popover
self.pop = nil; // release the popover, this forces to create a fresh popover each time
}
If you want you can also remove the line self.pop = nil; but remember to release it in dealloc (I suppose you are not using ARC code since you are using retainCount). As bbum suggested you should't use retainCount to check objects existence.
In addition, each UIPopoverController instance has a property called popoverVisible if you want to see if a popover is already visible or not.
OK, I did it. For others who find this question. In addition to the other answers, this is how I did it.
I made my view controller a UIPopoverControllerDelegate.
I then created an BOOL called myPopoverControllerOpen.
When I created my popover I set the BOOL to yes. Using the delegate method popoverDidDismissPopover I then set the BOOL back to NO.
I then check on the state of this BOOL before presenting the popover.

How can I dismiss keyboard in iPhone OS 3.2 with text field in popover?

Edit:Ok, this is weird... After doing extensive debugging, I have discovered that whilst the text fields are resigning first responder status (I can see that there is no longer a blinking bar in any), the keyboard is NOT GOING DOWN! Maybe this deserves a different question.
I have several text fields in a custom uiviewcontroller subclass, which is displayed within a popover. The popover is displayed form a bar button. I want the keyboard to go down when the popover is dismissed (either by the user tapping the bar button again, or tapping outside the popover. From the view controller that displays the popover, when the popover is dismissed, in either of the 2 fashions, I call
[optionsController dismissFirstResponder];
Optionscontroller is the uiviewcontroller subclass in the popover. Dismissfirstresponder is a method I defined:
-(void)dsimissFirstResponder {
[nameField resignFirstResponder];
[descriptionField resignFirstResponder];
[helpField resignFirstResponder];
}
Those are three IBoutlets which I connected in the xib to the text fields.
That doesn't work. Any help with this would be greatly appreciated.
The code is called as such:
[optionsController dismissFirstResponder];
[poppoverController dismissPopoverAnimated];
I set a breakpoint in dismissFirstResponder and it is called when I expected it to be. I also checked, and all three IBOutlets are non-nil during that function call. These are the only text fields in the whole app, so I'm not sure how else to put the keyboard down.
What you need is to receive the delegate method callbacks for a popover. Have you looked at the docs for the UIPopoverControllerDelegate? The following methods are defined:
-popoverControllerShouldDismissPopover:
-popoverControllerDidDismissPopover:
These should get called when your user does any gesture to dismiss the popover (tapping outside, etc.) assuming you've set a delegate for your popover and you've implemented this formal protocol in that delegate. When – popoverControllerDidDismissPopover: gets called, you can just call -resignFirstResponder on your controls at that point.
// In your popover delegate
- (void)popoverControllerDidDismissPopover:
(UIPopoverController *)popoverController
{
[nameField resignFirstResponder];
[descriptionField resignFirstResponder];
[helpField resignFirstResponder];
}

Catch a subview touch event

I have a UIViewController, and I've added two subviews to its view. One subview is the view of a UIViewController. The other subview is a UITextField.
I need to dismiss the keyboard for the UITextField when the user touches the other view, but I can't figure out how to detect those events. The UIViewController's tableView catches them and breaks the UIResponder chain, so my UIViewController never hears about them. I don't want to subclass everything in the hierarchy just so I can pass the event along up the chain, so what are my options?
I should mention that I'm doing everything programmatically, no IB.
Thanks guys.
So after digging into it, I don't think there's any other way. The responder chain starts with the UIView that received the touch event, if it's not caught passes to that view's controller, then to its superview and so on.
Obviously subclassing every UIView element in a UITableView is insane overkill for this situation.
What I did was create a transparent "touch shield" view with the same frame dimensions as my table view. When the keyboard expands, I add this view over my table, and when it collapses I remove it. This allows me to intercept those touches before they hit the table.

iPhone - Have the keyboard slide into view from the right like when editing a note in Contacts

I'm looking for a way to slide the keyboard into view from the right, like what happens in the Contacts application when you edit a note.
My problem is that when I call [someTextView becomeFirstResponder] in viewWillAppear, the keyboard immediatly pops up with no animation. And when I call it in viewDidAppear, the view first slides in from the right (UINavigationController does the sliding), and then the keyboard slides in from the bottom.
Is it possible to have the keyboard slide in from the right, together with the view?
Solution
In iOS 7, calling becomeFirstResponder on _textView in viewDidLayoutSubviews works.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[_textView becomeFirstResponder];
}
Note: Doing it in viewWillLayoutSubviews also works.
Explanation
Read the discussion in the docs for becomeFirstResponder.
You may call this method to make a responder object such as a view the first responder. However, you should only call it on that view if it is part of a view hierarchy. If the view’s window property holds a UIWindow object, it has been installed in a view hierarchy; if it returns nil, the view is detached from any hierarchy.
When using a navigation controller to push your custom view controller onscreen, self.view.window is still nil by the time either viewDidLoad or viewWillAppear: is called. So, _textView.window is also nil in the same methods, since _textView is a subview of self.view, i.e., they're both in the same window. No matter how you present your custom view controller, self.view.window (and thus _textView.window) is also nil in initWithNibName:bundle:. self.view.window is set by the time viewDidAppear: is called, but that's too late because by that time, the navigation controller has already completed the animation of pushing the view onscreen.
self.view.window is also set by the time either viewWillLayoutSubviews or viewDidLayoutSubviews is called and these methods are called before the push animation of the navigation controller begins. So, that's why it works when you do it in either of those methods.
Unfortunately, viewWillLayoutSubviews and viewDidLayoutSubviews get called a lot more than just on the initial navigation controller push. But, navigationController:willShowViewController: and willMoveToParentViewController: get called too soon (after viewDidLoad but before self.view.window is set) and navigationController:didShowViewController: and didMoveToParentViewController: get called too late (after the push animation).
The only other way I can think of doing it is to somehow observe the window property of _textView so that you get notified when it changes, but I'm not sure how to do that since window is readonly.
All you need to do is tell the text view in question to become the first responder in the viewDidLoad method of the view controller you're pushing onto the navigation stack:
override func viewDidLoad() {
super.viewDidLoad()
someTextView.becomeFirstResponder()
}
This works in iOS 8. The keyboard slides in from the right along with the view.
In iOS 7 (or any version before) you can make a simple thing in loadView, viewDidLoad or viewWillAppear
[yourTextView performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
In this case you will get left-to-right appearance of the keyboard aligned with the motion of pushing view controller.
For iOS 7 I've found the following solution to work the best for me:
-Import UIResponder-KeyboardCache to your project.
-Add [UIResponder cacheKeyboard:YES]; to the viewDidLoad of the view before the keyboard view. It might be better to do this immediately when the application loads or during a time convenient when you can afford it (during an HTTP request, for example). In most cases, simply in the view before is sufficient.
-Add the following to the viewDidLoad of the keyboard view.
dispatch_async(dispatch_get_main_queue(), ^{
[_textField becomeFirstResponder];
});
To explain, this will preload the keyboard view, which will remove the delay from the first call of the keyboard view. Calling becomeFirstResponder on the text field in the main queue causes it to slide in with the view instead of animating upward before the view slides in.
You could try sending the becomeFirstResponder message to the new view controller before you push it onto the stack. For example:
-(void)functionWhereYouPushTheNewViewController {
yourNewViewController *newVC = [[yourNewViewController alloc] init];
[newVC.yourTextView becomeFirstResponder];
[self.navigationController pushViewController:newVC animated:YES];
}
I have found that changing animations on things like they keyboard is pretty tough though, and if you read the Human Interface Guidelines Apple makes it pretty clear that they want certain things to act in certain ways, all the time. There are ways to change the behaviors of certain animations but they often involve undocumented API calls and are grounds for rejection from the app store. It would be a violation of HIG to have pushed views slide up from the bottom, for example.
Hope this helps.