When I change my uitableview to edit mode, i'd like the user to be able to select a cell without the segue taking place. The segue was linked in the storyboard. Is there a way to disable the segue during edit mode?
I can't disable interaction with the cell during edit more because I need to pick up on the editing control (insert button) press.
In your view controller, override:
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender{
return !self.isEditing;
}
Inside that method check if this is the correct segue, and verify the edit state. If the editing is on, return NO; otherwise, return YES.
Swift 3+:
Assuming you've inherited from UITableViewController:
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return !tableView.isEditing
}
Note this will disable all segues while editing. If you have more than one and you want some to remain enabled in edit mode, apply the appropriate logic within this function to return true or false as appropriate.
Related
Is it possible to redirect the becomeFirstResponder from self to a subview?
Here is my scenario.
I create a new UIWindow, let's call it secWin, object with a UINavigationController, let's call it navCon, as the rootViewController.
navCon has a custom UIViewController, let's call it cusViewCon, as rootViewController which contains only a single UITextField.
Now when i press a button in my main window, i call [secWin makeKeyAndVisible], which successfully shows the new window.
But now i want the UITextField to show the keyboard any time cusViewCon gets a becomeFirstResponder call, which is e.g. done by iOS by default when calling [secWin makeKeyAndVisible].
I tried to do all this by simply overwrite becomeFirstResponder in my cusViewCon class like this:
- (BOOL)becomeFirstResponder {
[_myTextField becomeFirstResponder];
return NO;
}
It is working, but i don't know if it is the correct way to do so, because in the documentation it says:
You can override this method in your custom responders to update your object's state or perform some action such as highlighting the selection. If you override this method, you must call super at some point in your implementation.
This method actually makes more sense when you code a bit for macOS where pretty much any view may become first responder. So don't worry to much about documentation.
Your procedure is on a correct track but I would do something like:
- (BOOL)becomeFirstResponder {
BOOL willBecomeFirstResponder = [super becomeFirstResponder];
if(willBecomeFirstResponder) {
return YES; // This element is taking over the first responder so do nothing
}
else {
[_myTextField becomeFirstResponder]; // This element may not become first responder at the moment so forward message to text field
return NO;
}
}
This way we ensure the execution of the superclass method still occurs. It should return false in your case (if not then also take a look into canBecomeFirstResponder method) so the text field should receive the message. If it does become first responder by design then it is best not to call it on your text field.
For those who also struggle with customisation of the responder chain in Swift, here is my take at the accepted answer. Might be useful for someone.
#discardableResult
override func becomeFirstResponder() -> Bool {
if super.becomeFirstResponder() { return true }
someView.becomeFirstResponder()
return false
}
#discardableResult
override func resignFirstResponder() -> Bool {
if super.resignFirstResponder() { return true }
someView.resignFirstResponder()
return false
}
someView in here is just your child view you are passing the event to. It can be UITextView, UITextField or any other UIResponder.
If you are using Swift, try this:
override func becomeFirstResponder() -> Bool {
return super.becomeFirstResponder() || myTextField.becomeFirstResponder()
}
I am stumped and I hope someone can help.
I am calling the resign first responder method for all five of my text fields prior to a segue. The segue occurs, if the keyboard was visible prior to the segue, the keyboard remains no matter what I do. This did not happen in IOS6. It is only happening in IOS7.
Thank you so much in advance for your assistance.
Here is the scenario:
The user touches one text field at time to enter data. The keyboard has no problems changing from first responder from one field to the next and can be resigned from the DONE button without issues. The problem comes when the user touches a field that will be populated from the picker view. If the keyboard was visible from one of the previous text fields, it won't go away.
I have this code attempting to resignFirstResponder on the editingDidBegin action of two of the fields. I am using these two fields to hold numbers but I am filling them from a picker on the next view.
- (IBAction)txtRatioOrHullTypeTouched:(id)sender
{
// Hide the keyboard before the segue to the picker occurs.
[self.txtPitch resignFirstResponder];
[self.txtRPM resignFirstResponder];
[self.txtSlipOrSpeed resignFirstResponder];
[self.txtRatio resignFirstResponder];
[self.txtHullType resignFirstResponder];
segueToPicker = YES; // Raise flag indicating that this segue is to the picker.
[self performSegueWithIdentifier:#"toPicker" sender:sender];
}
I also put this same code in the viewWillDisappear as shown here:
- (void)viewWillDisappear:(BOOL)animated // Unchanged
{
// Hide the keyboard before the segue to the picker occurs.
[self.txtPitch resignFirstResponder];
[self.txtRPM resignFirstResponder];
[self.txtSlipOrSpeed resignFirstResponder];
[self.txtRatio resignFirstResponder];
[self.txtHullType resignFirstResponder];
[super viewWillDisappear:animated];
}
Both of these methods are on the initial view, ViewController.m file.
I ended up here removing the text field causing the problem and replacing them with buttons. No scenario I tried (dozens) got this code to work as expected in IOS7, even though it all worked flawlessly in IOS6.
I tried all of the above and it worked as long as i dismissed the controller with a button. The function that was called when pressing the button could call the TextField's resignFirstResponder() function and all was well.
However, when an edge swipe was performed to dismiss the controller the keyboard kept popping up the next time I showed it. In my code I reuse the same controller between views. This might not be wise but, it's snappy!
After trying everything the internet had written (well not really, but pretty close) about this I found that i could implement the TextField's textViewShouldBeginEditing() and return false between the ViewControllers ViewDidDisappear and ViewDidAppear. It's ha hack, but it did the trick when nothing else worked.
I hope this helps you guys!
Swift code:
In my ViewController
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
myTextField.allowEdit = true
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
myTextField.allowEdit = false
}
In my TextField class
class MyTextField: UIView, UITextFieldDelegate {
var allowEdit = true
func textFieldShouldBeginEditing(textView: UITextView) -> Bool {
return allowEdit
}
}
You can call endEditing: on the view controller with the text fields. Your viewWillDisappear: method will look like this:
- (void)viewWillDisappear:(BOOL)animated
{
[self.view endEditing:YES];
[super viewWillDisappear:animated];
}
Contributing my 2 cents worth. dismissing keyboard correctly on iOS 9.2, a minimalist sample, FYI.
...
#property (assign) BOOL isTransitioning;
...
-(void)viewWillAppear:(BOOL) animated {
self.isTransitioning = YES;
}
-(void)viewWillDisappear:(BOOL) animated {
self.isTransitioning = YES;
}
-(void)viewDidAppear:(BOOL) animated {
self.isTransitioning = NO;
}
-(void)viewDidDisappear:(BOOL) animated {
self.isTransitioning = NO;
}
-(BOOL) textViewShouldBeginEditing:(UITextView*) tv {
if (self.isTransitioning) {
return NO;
}
return YES;
}
I think due to the way you are leaving the view through a picker, without going through an exit, you need to include the following in your viewController:
- (BOOL) disablesAutomaticKeyboardDismissal
{
return NO;
}
Swift, 2017
override var disablesAutomaticKeyboardDismissal: Bool {
get { return false }
set { }
}
So it seems now that the text field that controls the keyboard will not allow resignation. I used the canResignFirstResponder query on that field and the result (boolean) was FALSE. I also noticed that i get a flashing cursor in the field even after the resignFirstResponder is called. – Larry J Oct 25 '13 at 23:32
I know this is old, but I had a similar issue and wanted to share what worked for me in case it might help anyone else:
After reading the above comment I found that moving [self.view endEditing:YES] from where I had it in textFieldDidBeginEditing to textFieldSHOULDBeginEditing did the trick for me. Now the keyboard is dismissing properly before my segue.
Taking Zaheer's comment into Swift this works very well for me.
view.endEditing(true)
This is a problem i have frequently. My best method to cope is creating a clear button under the keyboard and having that call a dismiss helper. Control the clear button by toggling its isHidden property. Tapping outside the keyboard will hit that clear button and call the dismiss helper. What it won't do is trigger your segue, the user will need to tap again to navigate out but that keyboard will be gone.
in viewDidLoad():
var clearButton: UIButton!
self.clearButton = UIButton(frame: self.view.frame)
self.clearButton.backgroundColor = .clear
self.clearButton.addTarget(self, action: #selector(self.dismissHelper(_:)), for: .touchUpInside)
self.view.addSubview(self.clearButton)
self.clearButton.isHidden = true
Then add the dismiss helper:
func dismissHelper(_ sender: UIButton?) {
self.clearButton.isHidden = true
view.endEditing(true)
}
func displayClearButton(){
print("display clear button, hidden = false")
self.clearButton.isHidden = false
}
then on your textfield add the target
self.textField.addTarget(self, action: #selector(self.displayClearButton), for: .editingDidBegin)
I have an app having 7 screen. On the screen 7 i have a button that does valiation and submits data and then it jumps to screen 1 using modal segue.
But ,I only want to move screen1 if the validation succeeds or else i dont want to move to screen1.
Currently its moving to screen 1 independent of validation.
button click code is as below
- (IBAction)submitButtonActionForDemo:(id)sender
{
if (![JLTValidator validateFields:#[_authRepresentative, _acceptDeclarationStatement,_homeTeamRepName,_homeTeamRepPosition,_awayTeamRepName,_awayTeamRepPosition]])
{
// how to disable a modal segue here.
return;
}
}
JLTValidator is my validating class here.
Pls suggest/help. Thanks in adv.
If you want to only allow the segue some times, you need to "name" the segue in InterfaceBuilder, then implement this routine:
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([identifier isEqualToString:#"your segue name"]) {
return false;
}
return true;
}
I came across a strange bug in my app:
The setup
A simple Master-Detail app, iPhone style (ie. no split view, no popover, just a navigation controller, a table view controller, and a view controller).
The bug
Touch a "background" part of the table view (the darker grey parts on my screenshot) like a section header or footer.
While keeping your finger on the screen, touch a cell multiple times.
Release all fingers. The "detail" view will pushed normally, but when touching the back button, you will find that the detail view was stacked as many times as you touched the cell at step 2.
You can also touch multiple cells at step 2 and their destination views will be stacked in the correct order :)
Reproduce it
I was able to reproduce the bug with a clean, freshly created app, and on the last release of the Twitter app for iPhone (by touching the "Loading" label with finger #1 and touching a tweet multiple times).
However, I could not trigger the same behaviour in the Settings app, under the "General" tab (which is a grouped table view).
The bug was reproduced on iOS 6.0 and 6.1. I don't have devices with older versions to test.
Question
Is this a known trick when creating navigation/table view based apps and if so is there a solution to prevent this (weird) behavior ? Or is this an iOS bug (and if so, is it already known from Apple) ?
A possible stop-gap measure you could use is to implement
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
And use a boolean flag or something to indicate that you are currently trying to execute that segue. ex:
BOOL doingSegue = NO;
-(void) viewWillAppear
{
doingSegue = NO;
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
if ( [identifier isEqualToString:#"MySegueIdentifier"] )
{
if ( doingSegue )
{
return NO;
}
else
{
doingSegue = YES;
return YES;
}
}
return YES;
}
Swift Version
var doingSegue = false
override func viewWillAppear(_ animated: Bool) {
doingSegue = false
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "MySegueIdentifier" {
if doingSegue {
return false
}
else {
doingSegue = true
return true
}
}
return true
}
This is fixed by Apple in iOS 7.
For prior versions of the OS, Dan F's answer should do the trick.
I am running into an issue where the keyboard does not get dismissed when leaving a UITextField or UITextView in a UIModalPresentationFormSheet. In addition, I've created a large button to serve as the view's background so if the user taps outside the fields it gets triggered. I am using the same code in a regular view controller, and it works as expected. In the modal view controller it does nothing. Any suggestions would be appreciated.
- (BOOL)textFieldShouldReturn:(id)sender {
[titleTextField resignFirstResponder];
return YES;
}
- (BOOL)textViewShouldReturn:(id)sender {
[synopsisTextView resignFirstResponder];
return YES;
}
- (IBAction)textFieldDoneEditing:(id)sender {
[sender resignFirstResponder];
}
- (IBAction)textViewDoneEditing:(id)sender {
[sender resignFirstResponder];
}
- (IBAction)backgroundClick:(id)sender {
[titleTextField resignFirstResponder];
[synopsisTextView resignFirstResponder];
}
Overriding disablesAutomaticKeyboardDismissal to return NO as below fixed the same problem of mine. You should put this code to your view controller, from which you initiate the keyboard:
- (BOOL)disablesAutomaticKeyboardDismissal {
return NO;
}
Also, check this SO question if you want to get a detailed explanation.
For those having trouble with UINavigationController, I think there is a better solution than a category on UIViewController. We should change the behavior of UINavigationController to ask its topViewController (in my opinion, this is how all ViewController containers should handle this).
#implementation UINavigationController (DelegateAutomaticDismissKeyboard)
- (BOOL)disablesAutomaticKeyboardDismissal {
return [self.topViewController disablesAutomaticKeyboardDismissal];
}
If you're presenting a modal view with presentation style "form sheet", Apple apparently does not dismiss the keyboard, thinking that they don't want the keyboard to jump in and out where a user will be doing a lot of editing (i.e. "forms"). The fix would be to change presentation style or live with it.
If you implement the UITextFieldDelegate protocol you can inadvertently cause this behavior if you do text validation. If your validation codes returns false from textFieldShouldEndEditing when the text is invalid, the field can't relinquish it's firstResponder status and the keyboard will remain on screen in the next view.
More details at UITextField's keyboard won't dismiss. No, really
I solved this by resizing a UIModalPresentationPageSheet. See my answer here.
The disablesAutomaticKeyboardDismissal refused to work for me on iOS 7.
But... I managed to solve this issue by simply disabling the UITextFields on the screen.
My solution is described here.
This workaround even works on Modal UIViewControllers.
Yeah... it surprised me aswell !!
i have also facing same problem and also done everything but not thing works then i start thinking and get some result.
but this answer for those who want to dismiss keyboard on textfield click and then open pop up.
so all you need to call text field delegate
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == self.myTxtFieldName{
self.view.endEditing(true) // keyboard hide code
// here you can call your model or pop up code and keyboard will dismiss and your pop up open
return false
}
return true
}
Sorry if this is not working for you
if there is other answer then please edit it
Thank you