Attaching an object to a UIActionSheet
Asked Answered
W

3

2

A view's 'delete' button is pressed. The view belongs to a view controller, which handles the button press. However, that view controller is a child of a container view controller, so it sends its delegate a message that a deletion was requested, and includes the object that should be deleted.

The delegate (the parent view controller) receives the notification and presents a UIActionSheet to confirm the deletion. It also makes itself the delegate of that action sheet.

The user confirms the deletion, and parent view controller is ready to delete the object. Except it has to do this in actionSheet:didDismissWithButtonIndex:. By that point, it no longer knows which object was passed down from the child view controller.

Is there a way to attach an object to the alert sheet so that when it's dismiss action is fired, that object can be retrieved?

Westcott answered 6/10, 2012 at 19:25 Comment(0)
K
5

The Objective-C 2.0 runtime supports associated objects - using this API, you can, euh, associate object with each other using a key-value method. Example:

id someObject = // however you obtain it
objc_setAssociatedObject(theActionSheet, "AssociatedDelegateObject", someObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

// later try to get the object:
id someObject = objc_getAssociatedObject(theActionSheet, "AssociatedDelegateObject");
// process the associated object, then release it:
objc_removeAssociatedObjects(theAlertSheet);

Edit: it seems that you don't really need to shoot a bird usign a cannon and use runtime functions, since the same class/object that manages the alert sheet cares about the delegation also, so you could just assign it temporarily to an instance variable. However, this approach may be easier to extend later when your object model gets more complicated.

Koerlin answered 6/10, 2012 at 19:32 Comment(6)
I could also temporarily dump the object in an iVar, I suppose - what are the benefits of associating it to the alert sheet instead? (I acknowledge that my question specifically asked about 'attaching').Westcott
The same functionality you can achieve by subclassing UIAlertSheet, adding a retain property.Patisserie
@BenPackard no much benefits. I thought you need this because your question suggests that there's a so large distance in the object hierarchy/model that you can't directly access the two objects manipulating the alert sheet.Koerlin
@Patisserie Except that UIAlertView is not to be subclassed.Koerlin
@H2CO3 There is no UIAlertSheet. Nothing in the UIActionSheet documentation says not to subclass it. Perhaps you are thinking of UIAlertView.Madrid
Of course I thought UIAlertView :) my bad. Anyway, simply adding functionality, without overriding methods, won't do anything bad.Patisserie
L
2

You could use my ActionSheetDelegate class to create a Block that will act in the stead of the actionSheet:clickedButtonAtIndex: method. Since the Block will be created in the same context as the creation of the action sheet, it can capture the object that you wish to delete:

ActionSheetDelegate * delegate;
delegate = [ActionSheetDelegate delegateWithHandler:
    ^( UIActionSheet * sheet, NSInteger idx ){
        if( idx == [sheet destructiveButtonIndex] ){
            [self destroyObject:obj];
        }
        // Cancel button "falls through" to no action
}];

You can also "associate" an object with another arbitrary object, using the Associated Objects runtime facility. In essence, this allows you to add an ivar to any instance at any time.

// Set:
objc_setAssociatedObject(sheet, &key, objectToDestroy, OBJC_ASSOCIATION_RETAIN);

// Retrieve:
id objectToDestroy = objc_getAssociatedObject(sheet, &key);

This just requires you to have a key variable somewhere. The docs suggest a file-level static char, whose address is used, as I've done here. Any value which will not change between setting and getting will work, however.

La answered 6/10, 2012 at 19:33 Comment(0)
M
2

There are many ways to do this.

The simplest by far is just to give your parent view controller an instance variable that holds the object to be deleted. Since a UIActionSheet blocks other user interactions, it shouldn't be possible for the user to request deletion of a second object while the first object's deletion is pending.

The best way is to not present an action sheet at all, but just do the deletion and give the user an “undo” button.

You can use an associated object (see H2CO3's answer).

You can use a wrapper that lets you set up a block as the alert view's button handler (see Josh Caswell's answer or my own BlockActionSheet).

You can create a subclass of UIActionSheet and give it a property that holds the object pending deletion.

Madrid answered 6/10, 2012 at 19:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.