As a form of validation, is there any way to prevent an alert view from dismissing when pressing an "OK" button?
Scenario: I have 2 text fields in the alertview for username/password. If both are empty and the user presses "OK", I do not want the alert to be dismissed.
iOS 5 introduces a new property to UIAlertView to handle exactly this problem.
alert.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
Apple documentation on UIAlertView.
Add the new UIAlertViewDelegate method to handle the enabling/disabling of the button.
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
Apple documentation on UIAlertViewDelegate.
You’re doing it the wrong way, you should enable and disable the submit button according to the input. First you have to get access to the button. This is easy, just create the alert without buttons, create a standalone button and add it to the dialog:
[alert addButtonWithTitle:#"OK"];
UIButton *submitButton = [[alert subviews] lastObject];
[submitButton setEnabled:…];
And then you have to set a delegate for those textfields and enable or disable the button when the fields change:
- (BOOL) textField: (UITextField*) textField
shouldChangeCharactersInRange: (NSRange) range
replacementString: (NSString*) string
{
int textLength = [textField.text length];
int replacementLength = [string length];
BOOL hasCharacters = (replacementLength > 0) || (textLength > 1);
[self setButtonsAreEnabled:hasCharacters];
}
// Disable the ‘Return’ key on keyboard.
- (BOOL) textFieldShouldReturn: (UITextField*) textField
{
return NO;
}
Of course you should wrap all this into a separate class so that you don’t mess up your calling code.
I don't believe you actually need to pass in any button names. Just take out your OK button string and leave it as "nil".
Related
Can anyone help me in preventing dismissal of alertview on its button click event??
I have a textview as a subview of my alertView and i dont want to dismiss it if textview's value is nil.
As this is very old question,but I got one solution and though of posting if any other developer need in near future.
Implement protocol methods in .h file
In order to respond to button taps in our UIAlertView, we will use the – alertView:clickedButtonAtIndex: protocol method as
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
}
//Now below code will check if uitextfield value.
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
NSString *inputText = [[alertView textFieldAtIndex:0] text];
if( [inputText length] > 0)
{
//text field value is greater than zero ,then Done button will appear as blue,or else it will be blurred
return YES;
}
else
{
return NO;
}
}
Alternately, there is a much faster approach:
return [inputText length] ? YES : NO;
The does the same thing as the if statement does.
That might be against HIG Guidelines to NOT to dismiss an UIAlertView.
WORKAROUND : I don't know what goes on in your app, but to acheive this thing, what you could do is dismiss the AlertView and then check if textView's text is set or not. If it is set to nil, then bring up the alertview again!
i'm not sure you can.
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
is the only option available on callBack. And there's still at least one active button.
If you really need that specific behavior, try reimplementing your own UIAlertView
Im trying to add a UIAlertView to warn the user that the link will open in Safari. The user can then choose OK(open the url) or cancel which should just close the alert and return to the links.
I have three different UIButtons which has 3 different URLs.
Right now ive added a IBAction to all buttons and all buttons has Tags (which i think i can use somehow :D). I guess - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex: delegate will be good to use to..
My question: how should the UIAlertView know what URL to open i user clicks ok?
I suggest that you use a subclass of UIAlertView that is able to track some more properties along. I do this in all my projects and it is much simpler.
One solution to do this would be to subclass UIAlertView to MyAlertView and add a #property(nonatomic, retain) id userInfo; or #property(nonatomic, retain) NSURL* urlToOpen. Thus you can attach custom data to your UIAlertView and retrieve it in the delegate method to do whatever you need with it.
Another solution, and my preferred one actually, is to add Objective-C blocks support to UIAlertView, to be able to use UIAlertView using the blocks API instead of using a delegate. This is particularly useful if you use multiple UIAlertViews in the same class and with the same delegate, as using a single delegate to handle the different instances is a mess.
I personally use this technique all the time, as it also makes my code more readable by having the code that executes when the button is tapped right next to the code that shows the alert, instead of having it at a complete different places when you use delegate methods.
You can look at my OHAlertView subclass here on GitHub that implement this already. The usage is really simple and allow you to use blocks for each alert view instead of a common delegate, see below.
Usage Example
-(void)confirmOpenURL:(NSURL*)url
{
NSString* message = [NSString string WithFormat:#"Open %# in Safari?",
url.absoluteString];
[OHAlertView showAlertWithTitle:#"Open URL"
message:message
cancelButton:#"No"
okButton:#"Yes"
onButtonTapped:^(OHAlertView* alert, NSInteger buttonIndex)
{
if (buttonIndex != alert.cancelButtonIndex)
{
// If user tapped "Yes" and not "No"
[[UIApplication sharedApplication] openURL:url];
}
}];
}
Then each button can have its own action:
-(IBAction)button1Action
{
[self confirmOpenURL:[NSURL URLWithString:#"http://www.google.com"]];
}
-(IBAction)button2Action
{
[self confirmOpenURL:[NSURL URLWithString:#"http://www.stackoverflow.com"]];
}
Or you can have a common IBAction for all your buttons opening URLs:
-(IBAction)commonButtonAction:(UIButton*)sender
{
NSUInteger tag = sender.tag;
NSString* urls[] = { #"http://www.google.com", #"http://www.stackoverflow.com" };
NSURL* buttonURL = [NSURL URLWithString: urls[tag] ]; // in practice you should check that tag is between 0 and the number of urls to be sure, that's just an example here
[self confirmOpenURL:buttonURL];
}
Solved it like this:
added a tag when creating the UIAlertView. Like this:
- (IBAction)PressedButton {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Link"
message:#"Want to open in safari?"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
message.tag = 2; //different tag for each button
[message addButtonWithTitle:#"Cancel"];
[message show];
}
Then when - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex delegate was thrown I did this:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == alertView.cancelButtonIndex){
if (alertView.tag == 1)
{
//go to URL1
}
else if (alertView.tag == 2)
{
//go to URL2
}
else if (alertView.tag == 3)
{
//go to URL3
}
}
}
Your button action method should have a signature like this:
-(void)doSomething:(id)sender;
whereby sender will be the button. Based on this you could find out which URL is meant.
I have a quick question about the best method to check if all of my UIButtons have been pressed.
I have x number of UIButtons which I created programmatically.
Each button has its own unique tag (starting at 100 and incrementing upwards.)
When you click on a button is runs this:
- (void)myButtonAction:(id)sender
{
[self handleButton:sender];
}
- (void)handleButton:(UIButton *)button
{
// ???
}
If and only if the user has clicked on all buttons do a want an instance [self allButtonsClicked] to run.
What is the best way to do this? Should I make a NSMutableArray, and check to see if the tag number is in the NSMutableArray and if it is not, then add it.
And then when the NSMutableArray is equal in size to the x number of buttons then run [self allButtonsClicked].
What is the simplest method to make sure each and every button has been clicked?
*edit I figured it out after typing it out. Writing it out helped me get it.
-(void)letterreveal: (id)sender {
//data
UIButton *button = (UIButton *)sender;
//action
[self clickcheck:[NSNumber numberWithInt:button.tag]];
}
-(void)clickcheck:(NSNumber*)currenttag {
if ([self.buttonPressCounts containsObject:currenttag]) {
NSLog(#"case A");
}
else {
[self.buttonPressCounts addObject:currenttag];
NSLog(#"case B");
if([self.buttonPressCounts count]==[self.currentword length])
{
NSLog(#"fininshed");
}
}
}
buttonPressCounts is a NSMutablearray.
I just had to make sure to set it whenI made the buttons.
self.buttonPressCounts = [NSMutableArray arrayWithCapacity:[self.currentword length]];
currentword is a NSString (each button is a letter derived from the NSString).
You could create an NSMutableSet with all buttons and then remove each clicked button from that set until it is empty. Once the set is empty, you have certainly clicked all buttons.
If you dont mind, if a button was pressed once or more often, use a member ivar of NSMutableSet.
And I would use a number of the tag, but add /remove the button itself.
I'm trying to display a UIActionSheet from my iPad. Here's the code that I'm using:
-(void) presentMenu {
UIActionSheet *popupMenu = [[UIActionSheet alloc] initWithTitle:#"Menu" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:nil];
for (NSString *option in _menuItems) {
[popupMenu addButtonWithTitle:option];
}
popupMenu.actionSheetStyle = UIActionSheetStyleBlackOpaque;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
[popupMenu showFromTabBar:_appDelegate.tabBar.tabBar];
}
else if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[popupMenu showFromBarButtonItem:self.navigationItem.rightBarButtonItem animated:YES];
}
[popupMenu release];
return;
}
The iPhone version of the program displays all the buttons in _menuItems, but the iPad version just ignores the last item from that array. Does anyone know why this might be happening?
Thanks,
Teja.
Found the answer as soon as I typed out this post. Somehow removing the "Cancel" button causes both the buttons to come up. Weird.
EDIT: Although, this is really annoying because all my button indices change between the iPhone and the iPad versions (The iPhone still needs the cancel button). How do I handle this?
I think what iOS is doing is it's expecting the last button to be the cancel button (regardless of whether it is or not) and is removing it, but maybe only for iPads. This is probably because a user can tap outside the action sheet to dismiss it. The problem I have with Apple's design choice is that it may not always be evident that the dialog can or should be dismissed in that way.
For example, I am showing my action sheet by calling [actionSheet showInView:self.view]; This causes the entire view to be grayed with the action sheet displaying in the middle of the device. Users are going to--rightly, in my opinion--assume that they have to choose one of the buttons.
I understand there are other action sheet display mechanisms--like the one that displays it as a bubble attached to a bar button item--where a cancel button is obviously redundant. It would be nice if Apple allowed for more flexibility here. For my app, I am probably going to have to add a dummy button to the end of the array I'm passing into my custom constructor, knowing that iOS will hide it. If the behavior changes in a future release of iOS... well, I'll just have to address it at that time.
In your case, I recommend not using the constructor that takes cancelButtonTitle and destructiveButtonTitle. Instead, subclass UIActionSheet and add buttons manually using the method above. Then, set cancelButtonIndex and destructiveButtonIndex to the desired indices. Remember that you don't have to set those two properties; they default to -1 (no button). Also, remember to abide by the HIG regarding the position of your buttons.
Here's one of my subclass' constructors (edited for brevity), just to give you an idea:
- (instancetype)initWithTitle:(NSString *)title
buttonTitles:(NSArray *)buttonTitles
cancelButtonIndex:(NSInteger)cancelButtonIndex
destructiveButtonIndex:(NSInteger)destructiveButtonIndex
{
self = [super initWithTitle:title delegate:nil cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
if (self)
{
if (buttonTitles)
{
[buttonTitles enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
[self addButtonWithTitle:obj];
}];
}
self.cancelButtonIndex = cancelButtonIndex;
self.destructiveButtonIndex = destructiveButtonIndex;
if (self.cancelButtonIndex > -1)
{
[self addButtonWithTitle:#""];
}
}
return self;
}
I have 3 uitextfield in my project
I need to when tap inside one of them (uitextfield2 ) a custom subview appear , and need the key board to appear when tap on one another (uitextfield1 )
the problem is when I tab on uitextfield1 , the keypad appear and not go even I clicked return or tap on another uitextfield2
I need the keyboard to disappear when I click out of the uitextfield1 or when click return
I use the following code
- (BOOL)textFieldShouldReturn:(UITextField *)textField { // When the return button is pressed on a textField.
[textField resignFirstResponder]; // Remove the keyboard from the view.
return YES; // Set the BOOL to YES.
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
// [textField resignFirstResponder];
[self SelectModalityClick]; // will be called If i tapped inside the uitextfield2 to display the custom view
return NO;
}
When you create your UITextField instances, you can set the inputView to whatever UIView subclass you want with something like this.
UITextField *aTextField = [[UITextField alloc] initWithFrame:aRect];
aTextField.inputView = myCustomInputView; // Set this up in your viewDidLoad method
aTextField.delegate = self;
aTextField.tag = MyCustomInputView; // #define this as some integer
[self.view addSubview];
[aTextField release];
You shouldn't need to do anything special to make the correct input view appear, if you have a UITextField instance variable for the first responder, just assign it in textFieldShouldBeginEditing: and resign its first responder status in textFieldShouldReturn:.