I have two UIActionSheets and I am thinking that I will control them with one delegate (the UIViewController that instantiates them). The delegate will capture an actionSheet call and try to figure out which of the two threw the event.
I tried to get the modalView's title to differentiate, but it seems to be invalid...
Should this work?
If not, is there some other way to distinguish which UIActionSheet did the event?
Or do I need to create two different classes that will be separate delegates for each UIActionSheet?
Thanks in advance.
I think you need the tag property of the UIActionSheet.
Something like:
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle ... ];
actionSheet.tag = 10;
[actionSheet showInView:self.view];
Then in your delegate:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
switch (actionSheet.tag) {
case 10:
...
}
}
tag is a property of UIView and can be set in Interface Builder for components that appear there too. Quite handy, though I've never actually used it in this context myself.
Delegate methods in Cocoa include the sending object for this purpose. Keep a reference to each of your action sheets as an instance variable in your controller class, and you can compare this to the actionSheet parameter in your delegate methods to decide what actions you need to perform.
Using the view's tag property would work, but it would be easier to keep a reference. The tag property is meant to help you find a view if you're looking through a hierarchy of sub-views and don't have a reference to to the object you need.
You should use the actionSheet pointer passed to the delegate's method as Marc said. For example:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if(actionSheet == myDoSomethingActionSheet) {
if(buttonIndex == 0) {
[self doThingA];
return;
}
if(buttonIndex == 1) {
[self doThingB];
return;
}
}
if(actionSheet == myOtherActionSheet) {
if(buttonIndex == 3) {
[self doImportantThing];
return;
}
}
}
Related
In my app when the user clicks a button it calls a prepareForSegue. The app need to check a state and then prompt the user if they want to over write or delete. Problem is that it loads the next view controller before the UIActionSheet is displayed. How can I force the UIActionSheet to appear before the prepareForSegue is called? This is my logic for prepareForSegue;
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"mySegue"]) {
//Check Logic removed for simplicity
if ([myCheck count] > 0){
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:#"Save?"
delegate:self
cancelButtonTitle:#"Delete"
destructiveButtonTitle:#"Save"
otherButtonTitles:nil];
[actionSheet showFromToolbar:self.navigationController.toolbar];
}
}
}
Here is the Action sheet;
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == [actionSheet cancelButtonIndex])
{
//Delete logic removed
}else {
//Save logic removed
}
}
You won't be able to do it in a segue. You'll have to load the view controller manually in your action sheet handler when you get the button index that’s supposed to trigger it. That means whatever is triggering the segue now will have to be disconnected and pointed at an IBAction that will create and show your action sheet. prepareForSegue is intended to allow you to set parameters on your destination view controller prior to displaying it. The segue has already been set in motion by the time you get to prepareForSegue. At that point, there is no going back/canceling/delaying the performing of the segue.
Best Regards.
I have found it possible To peform a Segue from a action Sheet
As long as The segue Is from the View controller and and an Identifier here is a example.
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == [actionSheet cancelButtonIndex]) return;
if (buttonIndex == 0) {
[self performSegueWithIdentifier:#"visitWeb" sender:self];
}
if (buttonIndex == 1) {
if ([MFMailComposeViewController canSendMail])
{
Might not be the correct way as I'm a bit of a newb but for my modal view controller on StoryBoard it seems to do the trick
Creating an action sheet is an asynchronous event and does not block the segue. You'll need to trigger the segue from your action sheet.
How to switch views using UIActionSheet when cancel button is pressed ?
xcode 4.3.2
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
NSString *buttonTitle = [actionSheet buttonTitleAtIndex:buttonIndex];
if ([buttonTitle isEqualToString:#"Done"]) {
thiredViewController *controller1 = [[thiredViewController alloc] initWithNibName:#"thiredViewController" bundle:nil];
[self presentModalViewController:controller1 animated:NO];
}
}
this code do nothing, the view didn't change, my friend told me that i can't use this method to change views in the new Xcode , so what should i use ?
You should never compare the title of a button to decide which action to take. What if you decide to change it elsewhere but forget to change your delegate method as well? This will also get messy if you localize your app in multiple languages.
UIActionSheet has a convenient cancelButtonIndex method that you can use for this:
if (buttonIndex == actionSheet.cancelButtonIndex) {
//do your thing...
}
Have two actionsheet buttons and one modalviewcontroller on mainviewcontroller in application. Now for two actionsheet buttons and for modalviewcontroller, can i have multiple dismissviewdidfinish method for each
-(void)dismissViewDidFinish:(ModalViewController *)controller
{
[self dismissModalViewControllerAnimated:YES];
}
-(void)dismissViewDidFinish:(Devanagari *)controller1;
{
[self dismissViewControllerAnimated:completion];
}
-(void)dismissViewDidFinish:(English *)controller2;
{
[self dismissViewControllerAnimated:YES];
}
Cause if i add these three methods on mainviewcontroller i get red warning message duplicate declaration of method dismissviewdidfinish.
Any ideas how to solve this kind of situation.
You cannot have the same name for more than 1 method. Use a single dismissViewDidFinish:(UIViewController *)viewController method and then check to see which viewController finished:
- (void)dismissViewDidFinish:(UIViewController *)viewController {
//check to see what kind of class viewController is
//or use tags by setting the viewcontroller.view.tag when creating it
}
I hope this is pretty straight-forward. As you'll see by my code, I'm simply trying to get a UIAlertView button-press to pop me back to the root view.
I don't get any compile errors or warnings, and when I run the app, the "RedeemCoupon" method is called in the IBAction, and the UIAlertView pops up as it should, but it doesn't seem the "doneRedeeming" method gets called at all - I don't see anything from NSLog (yes I'm aware that I am setting buttonIndex to 0 - once I get this working I'll fix it). So, basically it doesn't work. I click the "cancel" button and the alert just goes away.
By the way I'm not sure if this matters, but this "RedeemCouponViewController" view is number 4 on the stack, and it was added by use of presentModalViewController in the previous view.
I'm open to other ways of doing this if needed - all suggestions welcome!
Thanks in advance!
// RedeemCouponViewController.h
#interface RedeemCouponViewController : UIViewController <UIAlertViewDelegate> {
// RedeemCouponViewController.m
- (IBAction) redeemYes: (UIButton*) sender {
CouponRedeem *redeem = [[CouponDatabase database] couponRedeem:_uniqueId];
[redeem release];
UIAlertView *doneRedeeming = [[UIAlertView alloc]initWithTitle:#"Coupon Redeemed!"
message:#"Thanks for shopping!"
delegate:self
cancelButtonTitle:#"Back to Main Menu"
otherButtonTitles:nil];
[doneRedeeming show];
[doneRedeeming release];
}
-(void) doneRedeeming: (UIAlertView *) doneRedeeming clickedButtonAtIndex: (NSInteger)buttonIndex {
if (buttonIndex = 0) {
NSLog(#"doneRedeemed method called");
[self.navigationController popToRootViewControllerAnimated:YES];
} else {
//do nothing
}
}
You want to have
if (buttonIndex == 0) {
in place of
if (buttonIndex = 0) {
The former checks for equality whereas the latter assigns.
Also, you want to have
– alertView:clickedButtonAtIndex:
where you have
- doneRedeeming:clickedButtonAtIndex:
You need to use UIAlertViewDelegate methods:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {}
not
-(void) doneRedeeming: (UIAlertView *) doneRedeeming clickedButtonAtIndex: (NSInteger)buttonIndex {}
Use the delegate method -alertView:didDismissWithButtonIndex: to listen for your cancel button index
#PengOne's answer is correct: your problem is this:
if (buttonIndex = 0) {
You said
I know it's not correct, but I just
wanted to be sure the statement was
true for now...
But buttonIndex = 0 evaluates to 0, making it equivalent to
if (0)
The code within that block will never execute, regardless of the value of buttonIndex. If you really want to do it unconditionally, change the if to if( 1 ), or just take the if out.
This would have been trivial to spot if you ran this code in the debugger. You might think you know what your code is doing, but if you don't watch it run, you don't.
I have a view controller that need to handle a number of UIAlertViews, so ideally I don't want to set the delegate to self, and handle everything with tags on the alert view - unless that is the preferred approach.
If I create a class implementing UIAlertViewDelegate, where/how can I safely release it?
-(void)myMethod {
MyUIAlertViewDelegate *myDelegate = [[MyUIAlertViewDelegate alloc] init];
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle ... delegate:myDelegate ...
[myAlertView show];
[myAlertView release];
}
What recommendations are there for creating this type of custom delegate?
I’m unsure what you stand to gain out of this. Are you creating a new instance of the class for each alert view? That seems like unnecessary overhead to me. Using tags doesn’t have to be messy, though; just use the delegate methods to call into other methods that do what you want:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
switch ([alertView tag]) {
case kWidgetATag:
[self doSomethingForWidgetA];
case kWidgetBTag:
[self doSomethingForWidgetB];
}
}
I think the best way to release your view controller would be in method that is called the last:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[self release];
}
It seems strange to have a class that exists solely to handle the UIAlertView's methods.
Typically some view controller will have direct interest in the result of the UIAlertView's result. That view controller will implement the UIAlertViewDelegate protocol, create and present the UIAlertView (setting delegate to self), and subsequently receive its delegate messages.
The ViewController will then update View or Model concerns as needed.