Re: Validation of fields in a navigation stack
Re: Validation of fields in a navigation stack
- Subject: Re: Validation of fields in a navigation stack
- From: Alex Zavatone <email@hidden>
- Date: Fri, 10 Jan 2014 10:53:13 -0500
I've had to deal with something that sounds similar in a UITableView earlier this week.
FYI, completing an edit by dismissing the keyboard commits the changed data to the datasource.
Simply tapping a field in a cell invokes editing.
Now, I'm not sure if this is "the best" way, or the standard way, but it is a way and it works.
Care needs to be taken as (from what I have seen in iOS 7), certain events are not sent to the desired objects (UITextField delegates) that you would expect to be sent, while others are. Notably, these are the events of a text field starting being edited, a text field ending being edited and a change within a text field.
As these edits on iOS will be handled through display of a keyboard, and possibly the clicking of the clear button, we need to make sure that the events for keyboard display and dismissal are also caught.
According to the research that I have done, if you make a UITableView that is not fullscreen or is in a UIView and extended by use of UITableView and UITextField delegates, you will have to register to receive notifications for all events that are not automatically sent to your view controller.
Like you, all I am checking for right now is "this text field is not empty", but once this condition is in place, it can be changed to be a rule set if you wish to handle other conditions.
All data that is displayed is linked from a singleton and committed back to it on the dismissal of the keyboard. A reloadData is then sent to the tableView, but really should just be sent to the cell.
One other thing is that I wasn't able to easily determine the current field being edited when receiving the textFieldDidChangeEditing message (if anyone knows, I'm all ears). This meant adding a property to the VC called fieldBeingEdited and when the field starts being edited, store a reference to that field and clear it when editing stops. Actually, since we will be constantly setting it when editing starts, you could never clear it, but that just seems sloppy.
To make sure the notifications are caught, I created two methods, configureForKeyboardDisplayNotifications and configureForTextEditNotifications.
In all the event methods that I care about, I call the updateUIOnEdit to conditionally format the UITextField's appearance (reddish if invalid, green border if legit).
These are:
-(void)textFieldDidBeginEditing:(UITextField *)textField;
-(void)textFieldDidEndEditing:(UITextField *)textField;
-(void)textFieldDidChangeEditing; // My custom method that is called as a result of subscribing to the UITextFieldTextDidChangeNotification
Don't add notifications for the UITextFieldDidBeginEditingNotification and UITextFieldDidEndEditingNotification, as these are already sent to the delegate, but for some reason, UITextFieldTextDidChangeNotification is not.
Notice that there is no textfield passed to textFieldDidChangeEditing. I'm not sure how to get the textField reference from the notification, so this is where I use the fieldBeingEdited reference to the currently edited textField.
In my cell subclass, I have the formatting code, so the viewController's updateUIOnEdit calls the cell's formatViewConditionally method.
Upon return to the viewController's class I can issue a reload data just for that cell.
A nice article to get you started which has a great method for getting a textField's parent cell is this one:
http://objcsharp.wordpress.com/2013/09/09/editable-uiTableviewcontroller/
Since I'm doing this for a tableView that is not full screen, there will be issues when the keyboard appears over the bottom cells in the table and the cells will be obscured by the keyboard. If you haven't tested for this, I'd certainly check that case. According to Apple docs, the process is to raise the contentInset for the bottom of the table view and then scrolling the tableView to the new position using an atScrollPosition:UITableViewScrollPositionMiddle. In my experience with a tableView that is not touching the top and bottom of the screen, this fails to scroll to the desired position using atScrollPosition:UITableViewScrollPositionMiddle or atScrollPosition:UITableViewScrollPositionTop and I have had to simply find a number that works and use that. Just an FYI.
Not sure how this would help in a UINavigationController, but this is what I have working in an editable UITableView.
How about disabling the back button if the UITextFieldDelegate returns no? Or simply setting enabled state of the back button to observe what the UITextFieldDelegate returns?
Cheers.
On Jan 9, 2014, at 7:22 PM, Rick Mann wrote:
> What's the right way to validate fields in an editable detail view in a navigation stack? It seems that the API doesn't really provide a good means to do so without a lot of contortions, and so I wonder what the intended behavior is.
>
> Here's what I've got: A UINavigationController stack with a UITableView that shows a list of Jobs. You can add a new job, or select an existing job, and it pushes a new UITableViewController that is a detail view of the Job. Some of the fields in this detail view are editable (i.e. "Name").
>
> I don't want to allow the user to specify an empty name. So I had a separate "Save" button in the top-right of the nav bar, and I would validate everything in there before actually saving the changes to Core Data. I also had to subclass UINavigationController to prompt the user to discard changes if they tapped the back button (I also had to do this if they did something else to dismiss the UIPopover all this lives in).
>
> For other reasons, I'd like to change this behavior a bit to avoid the save step all together, and to always save any change. But I still need to validate that they don't try to save an empty name. I'm trying to do this with -[UITextFieldDelegate textFieldShouldEndEditing:], in which I present an alert and return NO if the field is empty. But that doesn't work if they tap the "back" button. In this case, I'm content with simply undoing whatever change they made and not saving the changes without prompting, but I have to be able to detect that that's what's happening. There is no UINavigationControllerDelegate method for popping a view controller, only showing a new one.
>
> I guess I could set a flag in -viewWillDisappear: indicating that the field is ending editing because the view is going away, and save or discard (without an alert) in that case.
>
> What are your thoughts?
>
> --
> Rick
>
>
>
> _______________________________________________
>
> Cocoa-dev mailing list (email@hidden)
>
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden