How do I add accessibilityLabel to UIAlertView buttons?
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"Announcement"
message: #"message!"
delegate: nil
cancelButtonTitle: #"cancelButton"
otherButtonTitles: #"otherButton"];
[alert show];
According to Apple's documentation (search for 'Making Alert Views Accessible'), AlertViews are 'accessible by default'. This, and the fact that the buttons aren't editable, means that you probably shouldn't try changing the accessibilityLabels yourself. By default they use the button's title and the word 'button', which should be fine.
Accessibility for alert views pertains to the alert title, alert message, and button titles. If VoiceOver is activated, it speaks the word “alert” when an alert is shown, then speaks its title followed by its message if set. As the user taps a button, VoiceOver speaks its title and the word “button.” As the user taps a text field, VoiceOver speaks its value and “text field” or “secure text field.”
The only way you can do this is finding the UIButtons within the UIAlertView subviews:
for (id button in self.alertView.subviews){
if ([button isKindOfClass:[UIButton class]]){
((UIButton *)button).accessibilityLabel = #"your custom text";
}
}
However this is the only way to do it because there is not a public API to access these UIButtons and this is because Apple doesn't want you to access them. Accessing internal views of the UIAlertView class is something which Apple does not allow and it is likely that will make your app rejected during the App Store review process.
If you really need to have UIButtons with a custom accessibilityLabel you should look into designing a custom alert view instead of using the Apple UIAlertView class.
Late, but maybe useful to someone.
You can get access to the alert buttons using their index. For instance you can find the second button in the screen like the following way in Objective C:
- (void)getAlertActionButton:(XCUIApplication *)app {
// any code
XCUIElement *secondAlertButton = [[[app.alerts otherElements] buttons] elementBoundByIndex:1];
// any code
}
To find your alert window hierarchy use debug mode.
Related
I have a few ViewController subclasses inside a UINavigation controller. I have successfully used UIAlertViews elsewhere in the application, and I know how to set the delegate and include the correct delegate methods, etc.
In a ViewController with a UITableView, I have implemented a 'pull to refresh' with a UIRefreshControl. I have a separate class to manage the downloading and parsing of some XML data, and in the event of a connection error, I post a notification. The view controller containing the table view observes this notification and runs a method where I build and display an alert:
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Connection Error" message:[[notification userInfo] objectForKey:#"error"] delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
alertView.alertViewStyle = UIAlertViewStyleDefault;
[alertView show];
The alert displays correctly, but the cancelButton is unresponsive - there is no way to dismiss the alert! Putting similar code (identical, but without the notification's userinfo) in the VC's viewDidLoad method creates an alert that behaves normally.
Is the refresh gesture hogging first responder or something? I have tried [alertView becomeFirstResponder]. I would be grateful for any advice…
Update: screenshot included… is this the right info? (can't embed this image for lack of reputation) http://i.stack.imgur.com/4CGqS.png
Edit
It seems like you have a deadlock or your thread is stuck waiting. You should look at your code and see what causes this.
Original answer which lead to update in OP
Make sure the alert is shown on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
//Open alert here
});
This isn't a solution per se, but I might try two quick things as you troubleshoot:
1) Hardcode some text in the UIAlert, rather than passing in the notification object. See if there is any change in behavior.
2) Try adding another button to the alert and an accompanying method to catch it. So you'll see if the delegate is getting ny buttons messages at all.
Try adding a tag to the alertView
alertView.tag = 0;
Then create the method alertView:clickedButtonAtIndex: in the view controller.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 0) {
[alertView dismissWithClickedButtonIndex:0 animated:YES];
}
}
I am using a UIAlertView to prompt users for a password. If the password is correct, I'd like the alert view to go away normally; if the password is incorrect, I'd like the alert view to remain (with the text changed to something like "Please try again."). How do I alter the default behaviour so that the view doesn't go away when a button is pressed?
I've tried the subclassing method suggested at Is it possible to NOT dismiss a UIAlertView, but the dismissWithClickedButtonIndex wasn't being called in my subclass. After reading the documentation I see that subclassing UIAlertView isn't supported, so I'd like to try another approach.
If this isn't possible with UIAlertView, is the best option to simply make a password view from scratch? Thanks for reading.
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;
}
}
You could design a UIView similar to the alertview and can cal the functions inside this. Removing a UIView is controlled by you.
It's not possible to leave the alert there when a button has been clicked.
You can either disable the "OK" button by delegate method alertViewShouldEnableFirstOtherButton:, if you are checking the password locally, or just show another alert later. The second alert can contain a different information, e.g. "Password incorrect" instead of "Enter password".
I have a UIAlertView with multiple buttons. Is it possible to grey out and disable a button? I want the button to be visible but clear that it can't be pushed. Any suggestions are appreciated.
Make sure that you have enabled the current VC as implementing the <UIAlertViewDelegate> protocol, and then in your VC you could do the following:
- (void)viewDidAppear:(BOOL)animated {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert!" message:#"This is an alert view" delegate:nil cancelButtonTitle:#"Cancel!" otherButtonTitles:#"Off", #"On", nil];
alert.delegate = self;
[alert show];
}
/* UIAlertViewDelegate methods */
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView {
return NO;
}
// Other UIAlertViewDelegate methods...
Now why you would want to show a UIAlertView with a button which didn't have any functionality is a whole different question... :)
I hear of lots of people subclassing UIAlertView, but then I read this comment on Apple's UIAlertView Class Reference page:
Subclassing Notes
The UIAlertView class is intended to be used as-is and does not
support subclassing. The view hierarchy for this class is private and
must not be modified.
i.e. people should not be attempting to modify the elements or behavior of UIAlertView. It's likely that behavior can change in later versions of iOS (e.g. iOS 8 or iOS 7.1 or whatever), breaking their various modifications to UIAlertView.
Anyways, to answer your question: why not try creating your own UIView that you can add as UIAlertView-like subview on top of any of your views? That way, you'd have easy control over both the buttons and their behavior.
I'm a bit new to iOS development, and right now am working on some simple UI-related stuff. I have a UIAlertView that I'm using at one point to allow the user to enter some text, with simple Cancel and OK buttons. The OK button should be disabled if the text field is blank.
I added to my UIAlertViewDelegate an alertViewShouldEnableFirstOtherButton function, so the OK button would disable when there's no text, and I also set the UIAlertView's UITextField to have clearOnBeginEditing true, so the previous text would be gone every time I displayed the alert. Each of these things works perfectly on their own. Unfortunately, it seems like the AlertView is checking whether or not to enable the OK button before the text field is cleared, so when they're put together it comes up enabled. Below should be about the minimal code needed to reproduce.
-(void)viewDidLoad
{
textEntryBox = [[UIAlertView alloc] initWithTitle:#"Name" message:nil delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
[textEntryBox setAlertViewStyle:UIAlertViewStylePlainTextInput];
[textEntryBox textFieldAtIndex:0].clearsOnBeginEditing = YES;
}
-(IBAction)functionTriggeredByOtherLogic
{
[textEntryBox show];
}
-(BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
if(alertView == textEntryBox)
{
if([[alertView textFieldAtIndex:0].text length] > 0)
{
return YES;
}
else
{
return NO;
}
}
return YES;
}
So, ultimately, my question is this: am I doing something completely against the natural iOS way of doing things here? Is there a better way to do this? Should I just ignore the clearsOnBeginEditing property of the UITextField, and manually clear the Text property before showing the UIAlertView?
Try to set the textfield delegate to self
[[textEntryBox textFieldAtIndex:0] setDelegate:self]
and implement this method :
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[textField setText:#""];
}
I'm also having a UIAlertView with a textField to fill-in in my app, and it works for me
Using an alert view for this is probably a bit much. It might be easier if you use the master-detail paradigm and just push a new view controller where you can enter your values.
Ours is a health care app. We have a HIPAA-compliant speech recognizer in the app through which all the dictation can take place. The hospitals don't want physicians to accidentally start speaking to the Nuance Dragon server which is not HIPAA-compliant. So, I was looking for ways I could supress the dictation key on the keyboard.
I tried putting a fake button on the Dictation button on the key pad, but on the iPad the split dock concept keeps moving the microphone all over the screen. This does not sound like a reasonable solution. Are there any experts out there who could help me?
OKAY, finally got it! The trick is to observe UITextInputMode change notifications, and then to gather the identifier of the changed mode (Code seems to avoid the direct use of Private API, though seems to require a little knowledge of private API in general), and when the mode changes to dictation, resignFirstResponder (which will cancel the voice dictation). YAY! Here is some code:
Somewhere in your app delegate (at least that's where I put it)
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(inputModeDidChange:) name:#"UITextInputCurrentInputModeDidChangeNotification"
object:nil];
And then you can
UIView *resignFirstResponder(UIView *theView)
{
if([theView isFirstResponder])
{
[theView resignFirstResponder];
return theView;
}
for(UIView *subview in theView.subviews)
{
UIView *result = resignFirstResponder(subview);
if(result) return result;
}
return nil;
}
- (void)inputModeDidChange:(NSNotification *)notification
{
// Allows us to block dictation
UITextInputMode *inputMode = [UITextInputMode currentInputMode];
NSString *modeIdentifier = [inputMode respondsToSelector:#selector(identifier)] ? (NSString *)[inputMode performSelector:#selector(identifier)] : nil;
if([modeIdentifier isEqualToString:#"dictation"])
{
[UIView setAnimationsEnabled:NO];
UIView *resigned = resignFirstResponder(window);
[resigned becomeFirstResponder];
[UIView setAnimationsEnabled:YES];
UIAlertView *denyAlert = [[[UIAlertView alloc] initWithTitle:#"Denied" message:nil delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles:nil] autorelease];
[denyAlert show];
}
}
you can create your own keyboard and set the inputView for the text fields that will accept this dictation. then when they press any keys they will get your keyboard, therefore you dont have to override the keys on the standard keyboard, you will be able to customize the entire thing.
self.myButton.inputView = self.customKeyboardView;
here is an example of an extremely custom keyboard
http://blog.carbonfive.com/2012/03/12/customizing-the-ios-keyboard/
Ray also has a teriffic tutorial on custom keyboards.
http://www.raywenderlich.com/1063/ipad-for-iphone-developers-101-custom-input-view-tutorial
I hope that helps.
I had the same issue and the only way i found that hides the dictation button is changing the keyboard type.
For me changing it to email type seemed to be reasonable:
textField.keyboardType = UIKeyboardTypeEmailAddress;
You could make a subclass of UITextField/UITextView that overrides insertDictationResult: to not insert anything.
This won't prevent the information being sent, but you could then display an alert informing them of the breech.
This is a Swift 4 solution based on #BadPirate's hack. It will trigger the initial bell sound stating that dictation started, but the dictation layout will never appear on the keyboard.
This will not hide the dictation button from your keyboard: for that the only option seems to be to use an email layout with UIKeyboardType.emailAddress.
In viewDidLoad of the view controller owning the UITextField for which you want to disable dictation:
// Track if the keyboard mode changed to discard dictation
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardModeChanged),
name: UITextInputMode.currentInputModeDidChangeNotification,
object: nil)
Then the custom callback:
#objc func keyboardModeChanged(notification: Notification) {
// Could use `Selector("identifier")` instead for idSelector but
// it would trigger a warning advising to use #selector instead
let idSelector = #selector(getter: UILayoutGuide.identifier)
// Check if the text input mode is dictation
guard
let textField = yourTextField as? UITextField
let mode = textField.textInputMode,
mode.responds(to: idSelector),
let id = mode.perform(idSelector)?.takeUnretainedValue() as? String,
id.contains("dictation") else {
return
}
// If the keyboard is in dictation mode, hide
// then show the keyboard without animations
// to display the initial generic keyboard
UIView.setAnimationsEnabled(false)
textField.resignFirstResponder()
textField.becomeFirstResponder()
UIView.setAnimationsEnabled(true)
// Do additional update here to inform your
// user that dictation is disabled
}