As I type into a textbox on a UIAlertView, an ImageButton in the background starts shearing through. It only happens when the text gets close to the image.
The code for the alertview is as follows:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"... number"
message:nil
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok", nil];
[alert setAlertViewStyle:UIAlertViewStylePlainTextInput];
[[alert textFieldAtIndex:0] setKeyboardType:UIKeyboardTypeNumberPad];
[[alert textFieldAtIndex:0] becomeFirstResponder];
[alert setTag:1];
[alert setOpaque:true];
[alert show];
//EDIT
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 2)
{
NSString* title = [alertView buttonTitleAtIndex:buttonIndex];
if ([title isEqualToString:#"Cancel"])
{
[self doSomething1];
}
if([alertView.title isEqualToString:#"Are you sure?"] && [title isEqualToString:#"YES"])
{
[self doSomething2];
}
}
}
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
if (alertView.tag == 1){
NSString *inputText = [[alertView textFieldAtIndex:0] text];
if( [inputText length] == 10 )
{
NSUserDefaults* def = [NSUserDefaults standardUserDefaults];
[def setObject:inputText
forKey:#"FIELD"];
[def synchronize];
return YES;
}
else
{
return NO;
}
}
return YES;
}
I have tried setting it to opaque, I have tried looking for z-indexing values, and I have also tried scaling the image down to the smallest it needs to be. Taking screenshots does not help. This only happens in iOS 7.
UIAlertView are not shown in the same UIWindow as the rest of your view controllers. They are shown in their own UIWindow with a windowLevel of UIWindowLevelAlert. This other window is transparent (mostly) and renders on top of your main window. This makes UIAlertView objects not interact as well with other views you may have on screen. Do not modify any properties from the underlying UIView class.
The code you show above has if (alertView.tag == 1) as well as if (alertView.tag == 2), but it only ever creates a UIAlertView with a tag value of 1. Is there somewhere else you are creating a second alert? Are you possibly showing a second alert before the first one completes its disappearance animation? That has been known to cause graphical tears.
The delegate method -alertViewShouldEnableFirstOtherButton: can be called from within the -drawRect: method of the UIAlertView. The draw method must be extremely fast to avoid causing graphical tearing. Yours calls NSUserDefaults -synchronize, which performs a disk write operation. You should not perform any operation within your -alertViewShouldEnableFirstOtherButton: method, besides determining the response value. If you want to be notified of changes to the text field, assign yourself as the UITextFieldDelegate and implement -textField:shouldChangeCharactersInRange:replacementString:. That method should be safe to call NSUserDefaults -synchronize from.
As an additional tip, you could make your code much easier to manage using the PSPDFAlertView.
Related
In my application when a user clicks on the save button in the toolbar, the user is then prompted through UIAlertView for which way they would like to save their current work by choosing either to save as an image or save as a play. When the user selects save as a play they are then prompted with a 2nd UIAlertView which also has a text field for them to insert the name for the play. What I am trying to achieve is so that when no text is inputted, the Ok button is disabled and when the length of the text typed in is 1 or more, the file is then able to be saved (using archiver, this works correctly so this is not an isue) and the Ok button is then enabled. Listed below is the code that shows the two alert views, as well as what happens when different items from the views are selected.
- (IBAction)selectSaveType {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#""
message:#"Please Select an Option."
delegate:self
cancelButtonTitle:#"Save Play"
otherButtonTitles:#"Save to Photos", nil];
[message show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Save Play"])
{
NSLog(#"Save Play was selected.");
[self GetFileName];
}
else if([title isEqualToString:#"Save to Photos"])
{
NSLog(#"Save To Photos was selected.");
//here is where we need to find how to call saveDrawing.
[self saveDrawing];
}
else if([title isEqualToString:#"Ok"])
{
NSLog(#"OK selected");
UITextField *fName= [alertView textFieldAtIndex:0];
NSString *NameFile = fName.text;
[self savePlay:NameFile];
}
}
-(void)savePlay:(NSMutableString *)fileName{
//code here to save via archive.
NSArray *pathforsave = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [pathforsave objectAtIndex:0];
//here we need to add the file extension onto the file name before we add the name to the path
[fileName appendString:#".hmat"];
NSString *strFile = [documentDirectory stringByAppendingPathComponent:fileName];
[NSKeyedArchiver archiveRootObject:matView.drawables toFile:strFile];
}
I have been trying to use the following code below to handle this, but when the first UIAlertView fires (which is the asking to select a play - no text field present) - the function below then is ran and crashes the application since there are no text fields in the first alert view.
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
NSString *inputText = [[alertView textFieldAtIndex:0] text];
if( [inputText length] >= 1 )
{
return YES;
}
else
{
return NO;
}
}
alertViewShouldEnableFirstOtherButton is being hit when the first alert fires, then my application crashes in the simulator. Does anyone see why this would happen? Two things I am not so sure of
One - why the handle for the Ok button on the 2nd alert view to name the play is handled in the same block where the other buttons are handled. Since its a separate alert view, shouldn't it be in its own block?
Two - why alertViewShouldEnableFirstOtherButton is hit when it hasn't gotten to the 2nd alert view yet, it is called and runs with the first alert view, which crashes the app.
Thanks for your help, I am new to objective C.
Delegate methods for an alert view will be called for any alert view you present. That being said this code will crash because textFieldAtIndex:0 does not exist on a plain alert view. To solve this all you need to do is add an if statement to the delegate method identifying which alert called the action.
Edit: No longer identifies alert by name. Code now checks the style of the delegates sender.
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
if (alertView.alertViewStyle == UIAlertViewStylePlainTextInput) {
if([[[alertView textFieldAtIndex:0] text] length] >= 1 )
{
return YES;
}
else
{
return NO;
}
}else{
return YES;
}
}
I'm sort of stuck on a UIAlertViewStylePlainTextInput. (My code is too long to post here, but this is the part where the problem exists.) It all sort of works, but the program doesn't wait until the user enters the information before continuing. The "editName" button doesn't change the display in the code below, but the updateDisplay button works fine (if you've pressed the "editName" button before you pressed "updateDisplay"). How can I get everything to sort of halt until the user enters the info (or cancels). Or is there some other lead that will take me where I need to go. (I've been toying with putting the rest of the code in the -(void) alertView, but that just doesn't seem to be the correct way of doing it).
Thanks in advance.
#import "HSTestViewController.h"
#implementation HSTestViewController
#synthesize display;
NSString *newName;
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1)
{
newName = [[alertView textFieldAtIndex:0] text];
}
}
- (void) getNewName;
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"You made the High Score List"
message:#"Please enter your name"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok", nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[alert show];
}
- (IBAction)editName:(id)sender
{
[self getNewName];
newName = display.text;
}
- (IBAction)updateDisplay:(id)sender
{
display.text = newName;
}
#end
Add UIAlertViewDelegate to the class. And implement the below delegate method.
OK button index value will be 1 in your case.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex)
{
// call the method or the stuff which you need to perform on click of "OK" button.
}
}
Hope this helps.
I have multiple alert views in one view, and I use this code to detect which button was pressed:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if ([title isEqualToString:#"OK"]) {
//for one alert view
[passCode becomeFirstResponder];
} else if ([title isEqualToString:#" OK "]) {
//for another alert view, had to change "OK" to " OK "
[passCodeConfirm becomeFirstResponder];
}
}
Now since there are multiple alert views in one view that do different things, I have to trick the user into thinking "OK" and " OK " are the same thing. It works and looks fine, but it feels kind of messy. Surely there is another way to do this, such as making this specific to an alert view, and then making it specific to another. Do you know how I would do this? Thanks!
It would be more technical as well better that set unique tag for separate UIAlertView and identify it and access in its delegate method.
For example,
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"Message" message:#"Are You Sure you want to Update?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok",nil];
[alert setTag:1];
[alert show];
[alert release];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(alertView.tag == 1)
{
// set your logic
}
}
Use tag property to uniquely identify each of the alertview u create.
Like this
myAlertView.tag = 1
Then in the clickedButtonAtIndex delegate method check which alertview's button was clicked using this tag property ,
if(alertView.tag==1)
I wouldn't use the titles to distinguish between the buttons. You'll run into problems when your app is localized or you decide to change the button titles, but forget to update them everywhere. Use the button indexes instead or if you only have one button in addition to a cancel button, use the cancelButtonIndex property of UIAlertView.
To distinguish between multiple alert views, you could use their tag property.
In your view, add a property for each alert view.
UIAlertView *myAlertType1;
UIAlertView *myAlertType2;
#property (nonatomic, retain) UIAlertView *myAlertType1;
#property (nonatomic, retain) UIAlertView *myAlertType2;
Create your alert using these properties
self.myAlertType1 = [[[UIAlertView alloc] initWithTitle: ... etc] autorelease];
[self.myAlertType1 show];
Then in your delegate method:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (alertView == myAlertType1) {
// check the button types and add behaviour for this type of alert
} else if (alertView == myAlertType2 {
// check the button types and add behaviour for the second type of alert
}
}
Edit: Although the above works, iApple's suggestion of using the tag seems cleaner/simpler.
//in your .h file
UIAlertView* alert1;
UIAlertView* alert2;
//in your .m file
// when you are showing your alerts, use
[alert1 show]; //or
[alert2 show];
//and just check your alertview in the below method
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(alertView == alert1)
{
//check its buttons
}
else //check other alert's btns
}
I have a UIAlertView (several, in fact), and I'm using the method -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex to trigger an action if the user doesn't press cancel. Here's my code:
- (void)doStuff {
// complicated time consuming code here to produce:
NSString *mySecretString = [self complicatedRoutine];
int myInt = [self otherComplicatedRoutine];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"HERE'S THE STUFF"
message:myPublicString // derived from mySecretString
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Go On", nil];
[alert setTag:3];
[alert show];
[alert release];
}
and then what I would like to do would be the following:
- (void)alertView:(UIAlertView *)alertView
clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1) {
if ([alertView tag] == 3) {
NSLog(#"%d: %#",myInt,mySecretString);
}
}
}
However, this method doesn't know about mySecretString or myInt. I definitely don't want to recalculate them, and I don't want to store them as properties since -(void)doStuff is rarely, if ever, called. Is there a way to add this extra information to the UIAlertView to avoid recalculating or storing mySecretString and myInt?
Thanks!
Probably the quickest way to associate an object with an arbitrary other object is to use objc_setAssociatedObject. To use it correctly, you need an arbitrary void * to use as a key; the usual way to do that is to declare a static char fooKey globally in your .m file and use &fooKey as the key.
objc_setAssociatedObject(alertView, &secretStringKey, mySecretString, OBJC_ASSOCIATION_RETAIN);
objc_setAssociatedObject(alertView, &intKey, [NSNumber numberWithInt:myInt], OBJC_ASSOCIATION_RETAIN);
Then use objc_getAssociatedObject to retrieve the objects later.
NSString *mySecretString = objc_getAssociatedObject(alertView, &secretStringKey);
int myInt = [objc_getAssociatedObject(alertView, &intKey) intValue];
Using OBJC_ASSOCIATION_RETAIN, the values will be retained while attached to the alertView and then automatically released when alertView is deallocated.
I have recently begun my studies on iOS development so forgive me if I am asking something too obvious.
When my application's view loads it checks the configurations for some keys and if there's no value for these keys the application should display an alert and quit.
First of all, I have implemented the UIAlertViewDelegate:
#interface FirstViewController : UIViewController <UIAlertViewDelegate> {
...
And then checked for the settings:
- (void)viewDidLoad {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *url = [defaults stringForKey:#"url"];
NSString *apiKey = [defaults stringForKey:#"api-key"];
if([url length] < 1 || [apiKey length] < 1){
UIAlertView *dialog = [[[UIAlertView alloc]
initWithTitle:#"Not properly configured"
message:#"This application was not properly configured. Please configure the application on your iPhone settings."
delegate:self
cancelButtonTitle:#"Close"
otherButtonTitles:nil]
autorelease];
[dialog setTag:1];
[dialog show];
}
[url release];
[apiKey release];
[super viewDidLoad];
}
I understand that the method alertView didDismissWithButtomIndex should be called after the alertView's dismiss but for some reason this method is never called in my code.
- (void)alertView:(UIAlertView *)alertView didDismissWithButtomIndex:(NSInteger)buttomIndex {
if([alertView tag] == 1){
exit(0);
}
}
Any ideas on why this is happening?
didDismissWithButtonIndex is misspelled, you snuck an ‘m’ in there instead of ’n’.
You are listening for the wrong method, you should implement :
alertView:clickedButtonAtIndex:
In the doc you can read that the didDismissWithButtomIndex is called when dismissWithClickedButtonIndex:animated: is called on the alertView.
alertView:didDismissWithButtonIndex:
Sent to the delegate after an alert view is dismissed from the screen.
So for your code to work you should implementsomething like :
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if(buttonIndex == ...) {
// do something
}
}
PS: You shouldn't call exit(0), it is a bad practice on iOS to force an application to quit. User are supposed to quit the app with the home button.