Use NSArray to specify otherButtonTitles? - iphone

UIAlertSheet's constructor takes an otherButtonTitles parameter as a varg list. I'd like to specify the other button titles from an NSArray instead. Is this possible?
i.e. I have to do this:
id alert = [[UIActionSheet alloc] initWithTitle: titleString
delegate: self
cancelButtonTitle: cancelString
destructiveButtonTitle: nil
otherButtonTitles: button1Title, button2Title, nil];
But since I'm generating the list of available buttons at runtime, I really want something like this:
id alert = [[UIActionSheet alloc] initWithTitle: titleString
delegate: self
cancelButtonTitle: cancelString
destructiveButtonTitle: nil
otherButtonTitles: otherButtonTitles];
Right now, I'm thinking that I need to have a seperate call to initWithTitle: for 1 item, 2 items and 3 items. Like this:
if ( [titles count] == 1 ) {
alert = [[UIActionSheet alloc] initWithTitle: titleString
delegate: self
cancelButtonTitle: cancelString
destructiveButtonTitle: nil
otherButtonTitles: [titles objectAtIndex: 0], nil];
} else if ( [titles count] == 2) {
alert = [[UIActionSheet alloc] initWithTitle: titleString
delegate: self
cancelButtonTitle: cancelString
destructiveButtonTitle: nil
otherButtonTitles: [titles objectAtIndex: 0], [titles objectAtIndex: 1], nil];
} else {
// and so on
}
That's a lot of duplicate code, but it might actually be reasonable since I have at most three buttons. How can I avoid this?

This is a year old but the solution is pretty simple ... do as #Simon suggested but do not specify a cancel button title, so:
UIActionSheet *alert = [[UIActionSheet alloc] initWithTitle: titleString
delegate: self
cancelButtonTitle: nil
destructiveButtonTitle: nil
otherButtonTitles: nil];
But after adding your normal buttons, add the cancel button, like:
for( NSString *title in titles) {
[alert addButtonWithTitle:title];
}
[alert addButtonWithTitle:cancelString];
Now the key step is to specify which button is the cancel button, like:
alert.cancelButtonIndex = [titles count];
We do [titles count] and not [titles count] - 1 because we are adding the cancel button as extra from the list of buttons in titles.
You now also specify which button you want to be the destructive button (ie the red button) by specifying the destructiveButtonIndex (typically that will be the [titles count] - 1 button). Also, if you keep the cancel button to be the last button, iOS will add that nice spacing between the other buttons and the cancel button.
All of these is iOS 2.0 compatible so enjoy.

Instead of adding the buttons when you initialize the UIActionSheet, try adding them with the addButtonWithTitle method using a for loop that goes through your NSArray.
UIActionSheet *alert = [[UIActionSheet alloc] initWithTitle: titleString
delegate: self
cancelButtonTitle: cancelString
destructiveButtonTitle: nil
otherButtonTitles: nil];
for( NSString *title in titles)
[alert addButtonWithTitle:title];

addButtonWithTitle: returns the index of the added button. Set cancelButtonTitle to nil in the init method and after adding additional buttons run this:
actionSheet.cancelButtonIndex = [actionSheet addButtonWithTitle:#"Cancel"];

- (void)showActionSheetWithButtons:(NSArray *)buttons withTitle:(NSString *)title {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle: title
delegate: self
cancelButtonTitle: nil
destructiveButtonTitle: nil
otherButtonTitles: nil];
for (NSString *title in buttons) {
[actionSheet addButtonWithTitle: title];
}
[actionSheet addButtonWithTitle: #"Cancel"];
[actionSheet setCancelButtonIndex: [buttons count]];
[actionSheet showInView:self.view];
}

You can add the cancel button and set it like this:
[actionSheet setCancelButtonIndex: [actionSheet addButtonWithTitle: #"Cancel"]];

I know this is an old post, but in case someone else, like me, is trying to figure this out.
(This WAS answered by #kokemomuke. This is mostly a more detailed explanation. Also building on #Ephraim and #Simon)
It turns out the LAST entry of addButtonWithTitle: needs to be the Cancel button. I'd use:
// All titles EXCLUDING Cancel button
for( NSString *title in titles)
[sheet addButtonWithTitle:title];
// The next two line MUST be set correctly:
// 1. Cancel button must be added as the last entry
// 2. Index of the Cancel button must be set to the last entry
[sheet addButtonWithTitle:#"Cancel"];
sheet.cancelButtonIndex = titles.count - 1;

Related

XCODE - How to: UIAlertView (with a text field) on a loop until correct value entered into field?

I'm currently working in Xcode on an iOS app...
I've set up a UIAlertView (with a question as the message ) to pop up with a text field to retrieve a response.
The desired functionality is that upon entering an incorrect value into the text field, the UIAlert would loop... Until the correct response is entered. At this point, the UIAlert would be dismissed.
Heres what I have so far...
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification {
NSString* correctAnswer = #"2";
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Alarm"
message:#"1 + 1 ="
delegate:self
cancelButtonTitle: nil
otherButtonTitles:#"Continue", nil ];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
UITextField* answerField = [alert textFieldAtIndex:0];
answerField.keyboardType = UIKeyboardTypeNumberPad;
answerField.placeholder = #"answer";
[alert show];
// I feel like this would work, but I know it doesn't...
NSString *answerFieldString = answerField.text;
if ([answerFieldString isEqualToString: correctAnswer ])
{
[alert dismissWithClickedButtonIndex:-1 animated:YES];
}
}
I've done extensive google searching and can't come up with a solution... Any responses would be much appreciated!
try this...
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
NSString *answerFieldString = answerField.text;
if ([answerFieldString isEqualToString: correctAnswer ])
{
[alertView dismissWithClickedButtonIndex:-1 animated:YES];
}
}

How can I sent an array of strings into an UIActionSheet varargs init method?

I have an action sheet with options that vary depending on the circumstances. There are enough different button titles that I would like to construct an array of those button titles first, but I can't figure out how to convert that into the varargs format.
I want to do something like this:
NSMutableArray *buttonTitles = [NSMutableArray array];
if (condition1) {
[buttonTitles addObject: #"Do action 1"];
}
if (condition2) {
[buttonTitles addObject: #"Do action 2"];
}
if (condition3) {
[buttonTitles addObject: #"Do action 3"];
}
if (condition4) {
[buttonTitles addObject: #"Do action 4"];
}
UIActionSheet *actionSheet = [[[UIActionSheet alloc] initWithTitle: nil delegate: self cancelButtonTitle: #"Cancel" destructiveButtonTitle: nil otherButtonTitles: buttonTitles] autorelease];
Now obviously if I had to I could do something like this instead:
UIActionSheet *actionSheet = nil;
if (condition1 && condition2 && condition3 && condition4) {
actionSheet = [[[UIActionSheet alloc] initWithTitle: nil delegate: self cancelButtonTitle: #"Cancel" destructiveButtonTitle: nil otherButtonTitles: #"Do action1", #"Do action2", #"Do action3", #"Do action 4", nil] autorelease];
} else if (condition1 && condition2 && condition3 && !condition4) {
actionSheet = [[[UIActionSheet alloc] initWithTitle: nil delegate: self cancelButtonTitle: #"Cancel" destructiveButtonTitle: nil otherButtonTitles: #"Do action1", #"Do action2", #"Do action3", nil] autorelease];
}
// else ...
// about 14 other cases!
But that would be horrible. Anyone know some nice syntactic sugar to help me out?
EDIT:
It has been suggested that I use addButtonWithTitle, which on the face of it looks great, unfortunately it this puts the additional buttons after the cancel button, which isn't desirable.
I believe this is bug with Apple's code since their documentation on addButtonWithTitle states:
// adds a button with the title. returns the index (0 based) of where it was added. buttons are displayed in the order added except for the
// destructive and cancel button which will be positioned based on HI requirements. buttons cannot be customized.
HI requirements (Apple's own Human Interface guidelines) favor the cancel button below all other options, so I'd say Apple screwed up. Of course, that doesn't really help me, so I'm back to trying to convert between an NSArray and a varargs, which I still don't know how to do.
You can try this
NSMutableArray *buttonTitles = [NSMutableArray array];
if (condition1) {
[buttonTitles addObject: #"Do action 1"];
}
if (condition2) {
[buttonTitles addObject: #"Do action 2"];
}
if (condition3) {
[buttonTitles addObject: #"Do action 3"];
}
if (condition4) {
[buttonTitles addObject: #"Do action 4"];
}
[buttonTitles addObject: #"Cancel"];
UIActionSheet *actionSheet = [[[UIActionSheet alloc] initWithTitle: nil delegate: self cancelButtonTitle: nil destructiveButtonTitle: nil otherButtonTitles: nil] autorelease];
for (NSString *title in buttonTitles) {
[actionSheet addButtonWithTitle: title];
}
[actionSheet setCancelButtonIndex: [buttonTitles count] - 1];
A variation on Himanshu's answer:
UIActionSheet * as = [[UIActionSheet alloc] initWithTitle:#"Share"
delegate:self
cancelButtonTitle:nil /* don't set Cancel title here! */
destructiveButtonTitle:nil
otherButtonTitles:nil];
int cancelButtonIndex = 0; // will remain 0 if no other buttons besides "Cancel"
if ( canShareBySMS )
{
[as addButtonWithTitle:kSMSButtonText];
cancelButtonIndex++;
}
if ( canShareByMail )
{
[as addButtonWithTitle:kEmailButtonText];
cancelButtonIndex++;
}
/* etc. */
// after all other buttons have been added, include Cancel
[as addButtonWithTitle:#"Cancel"];
[as setCancelButtonIndex:cancelButtonIndex];
[as showInView:self.sharingViewController.view];
[as release];
Note: your UIActionSheetDelegate method should check against button titles instead of buttonIndex:
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex {
if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:kSMSButtonText]) {
//share via sms
}else if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:kEmailButtonText]) {
//share via mail
}
}
I think the key detail people miss is setting the cancelButton in the initWithTitle selector, instead of adding it after all the other buttons and specifying the look of the cancel button by calling setCancelButtonIndex:.
Why not just do something like this:
for (Nstring *button in buttonTitles)
[actionSheet addButtonWithTitle:button];
Here is a method I tried that seems to work just fine. This works if you want to add an additional button at the end of the buttons, before the "cancel" button.
NSString * optionalButton = nil;
if (condition4) {
optionalButton = #"Some Additional Option";
}
UIActionSheet * actionSheet = [[UIActionSheet alloc] initWithTitle:#"Some Title"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Button 1",#"Button 2",#"Button 3",optionalButton, nil];
[actionSheet showInView:self.view];
It appears to be okay to add an extra 'nil' at the end of the buttons list if your condition (condition4) is not met.
I've successfully tested this on iOS7.

Making selectedSegmentIndex select nothing after selection

I have a UISegmentControl that select nothing through IB, after the user selects the segment it becomes selected. How do i do it so that it doesnot gets selected?
//Show question method
-(void)question:(NSInteger)i
{
// Path to the plist
NSString *path = [[NSBundle mainBundle] pathForResource:#"Question" ofType:#"plist"];
// Set the plist to an array
NSArray *array = [NSArray arrayWithContentsOfFile:path];
//Check the number of entries in the array
NSInteger numCount = [array count];
if(i <numCount)
{ NSDictionary *dict = [array objectAtIndex:i];//load array index 0 dictionary data
self.title = [NSString stringWithFormat:#"Question %d", i+1];//set the nav bar title
quest.text = [dict valueForKey:#"Question"];//Set the Question to storage
ans.text = [dict valueForKey:#"Answer"];//Set the Answer to storage
NSInteger option = [[dict valueForKey:#"NumberOfOption"] integerValue ];//Check options to determine the question type
//check if the option is is a QRCode or Multiple Choices Question
if (option ==0)
{
QRbutton.alpha = 1; //show the QR Code Button If there is no options
OptionsAnswer.alpha = 0;//Hide Option if there is no options
}
else
{
QRbutton.alpha = 0.0;//Hide QR Code Button if there is options
OptionsAnswer.alpha = 1;//Show Option if there is options
[OptionsAnswer setTitle:[dict valueForKey:#"Option1"] forSegmentAtIndex:0];//Set Option Answer Value
[OptionsAnswer setTitle:[dict valueForKey:#"Option2"] forSegmentAtIndex:1];//Set Option Answer Value
[OptionsAnswer setTitle:[dict valueForKey:#"Option3"] forSegmentAtIndex:2];//Set Option Answer Value
[OptionsAnswer setTitle:[dict valueForKey:#"Option4"] forSegmentAtIndex:3];//Set Option Answer Value
[OptionsAnswer addTarget:self action:#selector(OptionAnswerCheck) forControlEvents:UIControlEventValueChanged];//Call action when options is being selected
}
}
else {
//if question is all answered, it will prompt an alert for end game video.
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Well Done"
message:#"You Have Answered All The Questions, Oh Wait A Minute I Heard A Cracking Sound...." delegate:self
cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease]; [alert show];;
[alert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:NO];
}
}
//Check if the selected Option is correct
-(IBAction)OptionAnswerCheck
{
//define a persistant location to save which question has been answered
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];//question storages
//pass the value from the selected option to a string
//NSString * selectedTitle = ([OptionsAnswer selectedSegmentIndex] >= 0) ? [OptionsAnswer titleForSegmentAtIndex:[OptionsAnswer selectedSegmentIndex]] :
NSString * selectedTitle = [OptionsAnswer titleForSegmentAtIndex:[OptionsAnswer selectedSegmentIndex]];
NSLog(#"Selected Title = %#",selectedTitle);//test
//check if the selected value is equal to the answers
if ([selectedTitle compare:self.ans.text] ==NSOrderedSame)
{
//Popup to say answer Correct
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Correct!"
message:#"Nice Work, Lets Move On To The Next Question" delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease]; [alert show];;
[alert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:NO];
//increase the question number
[self question:++currentQuestion];
//save increased question
[userDefaults setInteger:currentQuestion forKey:#"currentQuestion"];
}
else
{
//Popup to say answer Wrong
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Incorrect"
message:#"Close! But That's Not Right, Try Another Answer" delegate:nil
cancelButtonTitle:#"Try Again." otherButtonTitles:nil] autorelease]; [alert show];;
[alert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:NO];
}
//OptionsAnswer.selectedSegmentIndex = UISegmentedControlNoSegment;
}
Just search for setMomentary: in your developer documentation inside Xcode.
I'm not entirely sure what you're asking here, but I think that you want to set the momentary property toYES.
The property is in the inspector of IB as well. (Can't post a screenshot, I'm on my iPhone).

Writing a function for UIAlertView?

I'm sick of writing basic UIAlertView's, ie:
UIAlertView *alert = [[UIAlertView alloc] initWith...]] //etc
Instead of doing this, is it possible to put all this in a "helper" function, where I can return the buttonIndex, or whatever an alert usually returns?
For a simple helper function I guess you could feed parameters for the title, message, I'm not sure whether you can pass delegates in a parameter though, or bundle info.
In pseudo-code, it could be like this:
someValueOrObject = Print_Alert(Title="", Message="", Delegate="", Bundle="") // etc
Any help on this would be great.
Thanks
In 4.0+ you can simplify the alert code using blocks, a bit like this:
CCAlertView *alert = [[CCAlertView alloc]
initWithTitle:#"Test Alert"
message:#"See if the thing works."];
[alert addButtonWithTitle:#"Foo" block:^{ NSLog(#"Foo"); }];
[alert addButtonWithTitle:#"Bar" block:^{ NSLog(#"Bar"); }];
[alert addButtonWithTitle:#"Cancel" block:NULL];
[alert show];
See Lambda Alert on GitHub.
This is what I wrote, when I got sick of doing the same:
-(void)alert:(NSString *)title withBody:(NSString *)message firstButtonNamed:(NSString *)firstButtonName {
[self alert: title withBody: message firstButtonNamed: firstButtonName withExtraButtons: nil informing: nil];
}
-(void)alert:(NSString *)title withBody:(NSString *)message firstButtonNamed:(NSString *)firstButtonName informing:(id)delegate {
[self alert: title withBody: message firstButtonNamed: firstButtonName withExtraButtons: nil informing: delegate];
}
-(void)alert:(NSString *)title withBody:(NSString *)message firstButtonNamed:(NSString *)firstButtonName withExtraButtons:(NSArray *)otherButtonTitles informing:(id)delegate {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle: title
message: message
delegate: delegate
cancelButtonTitle: firstButtonName
otherButtonTitles: nil];
if (otherButtonTitles != nil) {
for (int i = 0; i < [otherButtonTitles count]; i++) {
[alert addButtonWithTitle: (NSString *)[otherButtonTitles objectAtIndex: i]];
}
}
[alert show];
[alert release];
}
You can't write a function that will display an alert and then return a value like a buttonIndex though, because that value-returning only occurs when the user presses a button and your delegate does something.
In other words, the process of asking a question with the UIAlertView is an asynchronous one.

What is "self" and how was the "view" property used?

In this:
-(IBAction)buttonClick: (id)sender {
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:#"Fo Sho?"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:#"fo sho"
otherButtonTitles:nil];
[actionSheet showInView:self.view];
}
A UIButton would be linked to this "buttonClick" IBAction but what is "self"?
self is the equivalent to this in many other languages such as C++. In other words when you call [myString length], the self pointer inside the length message is the pointer to your string named myString.
-(void)logScore
{
NSLog(#"%# score is %d", self.name, self.score);
}
[player logScore];
In the example, self is the player object.