I have two textfields for username and password and a submit button. When the submit button is pressed a check is performed to see if the username and password was typed or not. If not it shows an alert message and the field whose value was not entered becomes the first responder.
-(IBAction)loginPressed:(id)sender {
if ([username.text length] == 0)
{
[self showAlert:#"Invalid Username/ Password"];
[username becomeFirstResponder];
return;
}
if ([password.text length] == 0)
{
[self showAlert:#"Invalid Username/ Password"];
[password becomeFirstResponder];
return;
}
}
I observed that on clicking the button, the button remains selected for about 1.5 seconds and then the alert is shown. If I comment out the becomeFirstResponder method, it works without any pause. However I need becomeFirstResponder to be there. How do I speed things up using this?
Switch the ordering of becomeFirstResponder and showAlert.
[self showAlert:#"Invalid Username/ Password"]; will take a time. you cant speeup that thing.
Related
I have created a keyboard with some arrow keys that allows the user to navigate with arrow buttons, between all the text fields in the tableview. (2 per cell)
There are some situations where cells will be disabled, so I have a recursive call to skip them. ie. if the user presses the right arrow, and the textField is disabled, recursively call the right arrow again to skip it.
I also animate any scrolling that needs to be done myself. However, something has happened recently that is causing a EXEC_BAD_ACCESS when trying to set the firstResponder after a user navigates to a new textField, and the scrolling animation is finished.
I am able to reproduce this issue starting in the first cell and navigating down 2 times. Here is the code, in order of what is happening as best as I can display it, with a lot edited (like all the code for the other 3 arrow buttons so u only see the lines called)
//receive the arrow button pressed first
_arrowButton = arrowButton;
#try
{
switch (_arrowButton.tag)
{
case NumericKeyboardViewDownArrow:
destinationIndexPath = [NSIndexPath indexPathForRow:currentIndexPath.row + 1 inSection:currentIndexPath.section];
newTag = currentTextField.tag;
break;
default:
break;
}
// check bounds of the intended index path and return if out of bounds
if ((destinationIndexPath.row == -1) || (destinationIndexPath.row >= [_tableView numberOfRowsInSection:[destinationIndexPath section]])) {
destinationIndexPath = currentIndexPath;
return;
}
/*
Animation for scrolling
*/
CGPoint current = [_tableView contentOffset];
// if we must animate up
if (destinationIndexPath.row < currentIndexPath.row)
{
// up code here
}
// if we must animate down
else if (destinationIndexPath.row > currentIndexPath.row)
{
// always scroll animate for down
[_tableView setContentOffset:CGPointMake(0, current.y + _tableView.rowHeight) animated:YES];
}
// if we only move left or right
else
{
// left and right code here
}
//update our current location
currentIndexPath = destinationIndexPath;
#catch (NSException *e)
{
return;
}
then after the animation is finished in the did finish animating method
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
//after the animation is done, set the responder
nextTextFieldSelection = (UITextField *)[[_tableView cellForRowAtIndexPath:currentIndexPath] viewWithTag:newTag];
NSLog(#"nexttextfield%#",nextTextFieldSelection);
if (nextTextFieldSelection)
{
//CRASH OCCURS HERE [nextTextFieldSelection becomeFirstResponder];
}
[self checkTextFieldIsDisabled];
}
By the end of that method I am in a new text field and we check if disabled. In reproducing this crash, it just exits because the textFields I am moving in to are not disabled.
Ive tried resigning the first responder before calling a new becomeFirstResponder but that doesn't seem to have any effect. Its possible I wasn't resigning in the proper location though. Any ideas where the memory issue would be?
Thanks
I'm using an AlertView with a UITextView subview to let users reply to posts in my app, but I want the Reply button of the alert to disable when the user types more than the character limit. Will disabling the alert view button like this get my app rejected, is there a better way to do this?
-(void)textViewDidChange:(UITextView *)textView {
if (!replyAlert) {
return;
}
//character count
replyAlert.title = [NSString stringWithFormat:#"Reply to Post (%i/250)", [textView.text length]];
if ([textView.text length]>=250) {
//disable alert view button
for (UIView* view in [replyAlert subviews])
{
if ([[[view class] description] isEqualToString:#"UIAlertButton"])
{
UIButton *button = (UIButton*)view;
if ([button.titleLabel.text isEqualToString:#"Reply"]) {
//disable
button.enabled = NO;
}
}
}
} else if ([textView.text length]==249) {
//re-enable button if user deleted a character
for (UIView* view in [replyAlert subviews])
{
if ([[[view class] description] isEqualToString:#"UIAlertButton"])
{
UIButton *button = (UIButton*)view;
if ([button.titleLabel.text isEqualToString:#"Reply"]) {
//enable
button.enabled = YES;
}
}
}
}
}
Have a look at this method on the delegate (UIAlertViewDelegate)
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
This method will be called each time a user types a character into a text field in the alert view, assuming you are using the UIAlertViewStylePlainTextInput (?). So in this method you could check the length of the text in the text field and return TRUE/FALSE accordingly.
The method is only available in iOS 5.0 or later too which may be an issue if supporting older versions.
If you are adding your own text fields as subviews to the alert view, then this alone is cause for the app to be rejected as it states that the view hierarchy is not to be manipulated. If you are using the text input style alert view out-of-the-box and just navigating the subviews to check the button titles and disable them, I'd be surprised (note this is a subjective opinion) if that caused a rejection of the app.
I have an app that is continuously taking in images from the video buffer (using the process described here: http://www.benjaminloulier.com/articles/ios4-and-direct-access-to-the-camera) and performing various processing on the most recent image in the buffer. For my particular application, when something noteworthy is found in the image, I want to display this information to the user and have the user decide whether the information is correct or not.
I want to display 2 UIButtons on the screen when this information is returned and it is at this point that I wish the code to "pause" (like a runtime breakpoint) and wait to see which button the user clicks before resuming. After clicking a button, the buttons will disappear.
The reason for this is I can't have the camera continue to acquire images and process them while I am waiting for the user input.
Thanks!
EDIT:
Here is what my code basically looks like:
if (continueRunningScript == YES) {
NSString *results = [self processImage];
[self displayResults: results];
// Show pause button
dispatch_async(dispatch_get_main_queue(), ^{
[pauseButton setHidden: NO];
});
}
and the pause button code:
- (UIAction) pauseButtonPress:(id) sender {
[pauseButton setHidden: YES];
[playButton setHidden: NO];
continueRunningScript = NO;
}
and the play button code:
- (UIAction) playButtonPress:(id) sender {
[playButton setHidden:YES];
continueRunningScript = YES;
}
Where could I add more boolean to handle the delay?
I have alert view having 2 buttons "OK" and "Cancel" and a textfield.
Now i want to disable "OK" button until user enter some text in textfield.
How can i do this?
thanks in advance
UPDATE 2: For Swift 5.1
<#your alert controller#>.addTextField {(tf) in
//... set your tf characteristics i.e .keyboardType here
NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification,
object: tf,
queue: OperationQueue.main) { _ in
//enable or disable the selected action depending on whether the textField text is empty
<#your alert controller#>.actions[0].isEnabled = !tf.text!.isEmpty
}
}
posting this to update the response since ios 5 :
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
UITextField *textField = [alertView textFieldAtIndex:0];
if ([textField.text length] == 0)
{
return NO;
}
return YES;
}
UPDATE:iOS 8 Since Apple have deprecated the UIAlertView in favour of the UIAlertController. There is no longer a delegate call to alertViewShouldEnableFirstOtherButton:
So instead you would set the buttons enabled property via the UITextFieldTextDidChangeNotification
Add a textView to the alert with
(void)addTextFieldWithConfigurationHandler:(void (^)(UITextField *textField))configurationHandler
[<#your alert#> addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.delegate = self;
textField.tag = 0; //set a tag to 0 though better to use a #define
}];
Then implement the delegate method
(void)textFieldDidBeginEditing:(UITextField *)textField
- (void)textFieldDidBeginEditing:(UITextField *)textField{
//in here we want to listen for the "UITextFieldTextDidChangeNotification"
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textFieldHasText:)
name:UITextFieldTextDidChangeNotification
object:textField];
}
When the text in textField changes it will invoke a call to "textFieldHasText:" and pass along a NSNotification*
-(void)textFieldHasText:(NSNotification*)notification{
//inside the notification is the object property which is the textField
//we cast the object to a UITextField*
if([[(UITextField*)notification.object text] length] == 0){
//The UIAlertController has actions which are its buttons.
//You can get all the actions "buttons" from the `actions` array
//we have just one so its at index 0
[<#your alert#>.actions[0] setEnabled:NO];
}
else{
[<#your alert#>.actions[0] setEnabled:YES];
}
}
Don't forget to remove your observer when done
I wanted to extend the answer by Ryan Forsyth by adding this. If you add a Default styled UIAlertView, you can get an out of range exception if you try to access a textfield as none exist, so check your view style first.
-(BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView*)alertView
{
if(alertView.alertViewStyle == UIAlertViewStyleLoginAndPasswordInput ||
alertView.alertViewStyle == UIAlertViewStylePlainTextInput ||
alertView.alertViewStyle == UIAlertViewStyleSecureTextInput)
{
NSString* text = [[alertView textFieldAtIndex:0] text];
return ([text length] > 0);
}
else if (alertView.alertViewStyle == UIAlertViewStyleDefault)
return true;
else
return false;
}
You can create two buttons for Ok and Cancel.Then add those two as sub views in the UIAlertView.Now by checking the text(text length) in the textfield,You can perform enable and disable actions.
Without knowing the context of your application, the following may not apply - but have you read the iOS Human Interface Guidelines? It sounds as though you may be better off finding an alternative to UIAlertView if this is something that's going to be displayed to the user often.
Not really related to your question, but do not modify default UIAlertView if you don't want your app to be rejected. If I'm not wrong, you're adding textfields to the alertview, don't you? Like a login view. You should create your own view.
So, regarding your question, create your view, set the buttons has disabled, and delegate the UITextFields. When
- (void)textFieldDidBeginEditing:(UITextField *)textField;
is called, enable those buttons.
There is a text field to enter PIN in my login form. When i press "login" button i call the following method:
* (IBAction) loginBeforeAction:(id) sender {
[pin resignFirstResponder];
[progressView performSelectorInBackground:#selector(startAnimating) withObject:nil];
[self login];
}
but i the number pad is not hiding before the control moves to login method. In effect, i am can see the progress view with the number pad up. Is there any way to hide the number pad first and then show progress view ? plz help
Yes, The UI won't update until you get through the runloop. Meaning, your UI doesn't update until your login method finishes. Also, no need to update your progressView in the background either.
So, just delay the calls:
[progressView performSelector:#selector(startAnimating) withObject:nil afterDelay:0.1];
[self performSelector:#selector(login) withObject:nil afterDelay:0.25];