How do you pass a variable to the UIAlertView delegate? - iphone

How do you pass a variable to the UIAlertView delegate?
I have a variable that I want to use in the alert view delegate. It is only used in the function that shows the UIAlertView and the UIAlertView delegate, so i don't think it should be a property on the controller. Is there a way to attach the variable to UIAlertView and retrieve it in the delegate?
- (void) someUserCondition:(SOCode *)userCode {
if ([userCode warrentsConfirmation] > 0) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Title" message:#"Are you sure?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK",nil];
[alert setAlertViewStyle:UIAlertViewStyleDefault];
//TODO somehow store the code variable on the alert view
[alert show];
}
}
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if ([title isEqualToString:#"OK"]){
SOCode *userCode = //TODO somehow get the code from the alert view
[self continueWithCode:code];
}
}

in .h before interface:
extern const char MyConstantKey;
#interface ViewController...
in .m import:
import <objc/runtime.h>
in .m before implementation
const char MyConstantKey;
in .m implementation
-(void)viewDidAppear:(BOOL)animated{ //or wherever
NSString *aString = #"This is a string";
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Testing" message:#"test is test" delegate:self cancelButtonTitle:#"Okay" otherButtonTitles:nil];
[alert show];
[alert release];
objc_setAssociatedObject(alert, &MyConstantKey, aString, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
in .m alertview callback
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
NSString *associatedString = objc_getAssociatedObject(alertView, &MyConstantKey);
NSLog(#"associated string: %#", associatedString);
}

Use Associated Objects. It is described in more detail here: Your New Friends: Obj-C Associated Objects
To set the object you use use:
objc_setAssociatedObject(alert, &key, userCode, OBJC_ASSOCIATION_RETAIN);
And then to get it back:
SOCode *userCode = objc_getAssociatedObject(alertView, &key);
You also need to add static char key; so that it is in the scope of moth methods.
Update
I have wrapped this into a category on UIAlertView. You can use Cocoapods to bring it in:
pod 'HCViews/UIAlertViewHCContext', '~> 1.2'
The source is available here: https://github.com/hypercrypt/HCViews/blob/master/Categories/UIAlertView%2BHCContext.h

A lot of posts talk about the concepts behind associated objects (which is good!) but sometimes you just want to see the code. Here's a clean and quick category that you can either put in a separate file or above the interface of one of your existing .m files (you could even replace UIAlertView with NSObject and effectively add a context property to any object):
#import <objc/runtime.h>
#interface UIAlertView (Private)
#property (nonatomic, strong) id context;
#end
#implementation UIAlertView (Private)
#dynamic context;
-(void)setContext:(id)context {
objc_setAssociatedObject(self, #selector(context), context, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(id)context {
return objc_getAssociatedObject(self, #selector(context));
}
#end
And then you'll be able to do something like:
NSObject *myObject = [NSObject new];
UIAlertView *alertView = ...
alertView.context = myObject;
IMPORTANT:
And don't forget to nil the context in dealloc!!

UIAlertView is a subclass of UIView which has a tag property you can set to an integer. Unfortunately if you need something other than an integer to identify/pass info to the delegate than you will need to set some properties (or set up an array with the tag indexing into it) on the delegate itself. Advaith's way will probably work but is technically not supported by Apple.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Title" message:#"Are you sure?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK",nil];
[alert setAlertViewStyle:UIAlertViewStyleDefault];
alert.tag = SOMEINTEGER;
[alert show];

I suspect the most straight-forward way is a property in the alert view's delegate class. An alert view doesn't have any provision for "user info" and doesn't support sub-classing, which removes the only shortcuts that come to mind.

Subclass UIAlertView, add a property called userInfo with type of your choice. Set the user info value at the time you create an instance of Subclassed UIAlertView, and retrieve it from within the delegate method. (There you will get the subclassed instance which holds the userInfo)

Related

Simple iPhone jailbreak tweak not working

#import <UIKit/UIAlertView.h>
#class NSObject;
#interface SBIconController : NSObject
+ (SBIconController *)sharedInstance;
- (BOOL)isEditing;
#end
%hook SBIconController
-(void)iconTapped:(id)tapped {
SBIconController *sbic = [objc_getClass("SBIconController") sharedInstance];
if ([sbic isEditing]) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Message" message:[NSString stringWithFormat:#"%#", tapped] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
}
%orig;
}
%end
Above is a simple tweak that I have created with Logos. For some reason after installing, nothing is working, I just can't figure out what the problem is and How can I solve this problem?
Other questions that I have are:
Why do we declare class like SBIconController when there's already a SBIconController class?
Why do we declare it as a subclass of NSObject?
Why don't we just type in SBIconController when we're calling the [SBIconController sharedInstance] instead of [objc_getClass("SBIconController") sharedInstance]?
Thanks a lot for your help!
The code is fine. I tested it (I don't use logos) and iconTapped: method is indeed called when you tap an app icon. But what are you trying to achieve with isEditing? This property indicates whether you are editing SpringBoard (tap and hold an app icon) and when it equals YES method iconTapped: is NOT called when icon is tapped. It's called only when isEditing equals NO. So I suggest you insert alert without if ([sbic isEditing]) to test whether your tweak is working.
As for your other questions:
When dealing with private APIs we don't have headers and will get warnings/errors if we try to use them. In your case it's SBIconController. To solve this problem we can either download headers that others dumped using various tools like class-dump or declare these private APIs yourself. In your case it's latter.
Because SBIconController inherits from NSObject.
You can do it either way. Of course, when you have class declaration you don't need to use objc_getClass. And in your case you don't even need either of this things. You can just use self like you would in any other obj-C method. Your code will look like this:
%hook SBIconController
-(void)iconTapped:(id)tapped {
if ([self isEditing]) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Message" message:[NSString stringWithFormat:#"%#", tapped] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
}
%orig;
}
%end

UIAlertView crash by adding clickedButtonAtIndex

I create a class to call UIAlertview show on my screen. I write the UIAlert function in another class. Both these two classes are not my viewController class.
I use this UIAlert, which is a UITextfield inside, to store texts into a plist file.
here is the class to call UIAlert:
#import "Story.h"
#implementation Story
...
+ (void)stage1
{
AlertClass *pointer = [AlertClass new];
[pointer doAlert];
}
here is the class AlertClass.m file:
- (void)doAlert
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Title" message:#"Message" delegate:self cancelButtonTitle:#"Done" otherButtonTitles:nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[alert show];
}
//this makes crash!
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
self.storyFlow.text = [alertView textFieldAtIndex:0].text;
}
Before I add UIAlertViewDelegate in the .h and override the method "clickedButtonAtIndex", it works great. However, I need to store some data from the UITextfield inside the alert view. I get crash and don't know the message it responds as following.
Please help me to solve this problem. Thanks.
[crash pic] https://dl.dropbox.com/u/47381923/crash.tiff
do an NSLog on the text you get back from the Alert View to see whether that is the crash or the subsequent 'self.storyFlow.text = ' is causing it. Perhaps self.storyFlow has not been created yet (with alloc/init)

Using UIAlertView in an NSObject

I'm having a terrible time getting a UIAlertView to work within my custom NSObject class. In the research I've done it appears it should be possible but here's what I've run into.
First, here's my code:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(#"clickedButtonAtIndex: %d", buttonIndex);
}
-(void)testAlertView {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"List Contains Items"
message:#"List contains items. Remove all items & delete?"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
[alertView show];
}
If I set the delegate to self this code crashes as soon as I tap a button. If I set it to nil clickedButtonAtIndex is never called. I've tried with and without using the <UIAlertViewDelegate>.
I know someone will ask 'why are you doing this in NSObject instead of in your UIViewController?'. Primarily because I want to separate this code out so I can use it from multiple places in my app. But also because this is a small piece of a larger block of logic that makes sense to be on it's own.
Any ideas what I'm doing wrong?
Thanks,
Rich
I had the same problem using ARC. The root of the problem was the same. I solved it by putting my custom NSObject into a "strong" property to make sure the object exists as long as the calling object (an UIVIewCOntroller in my case) exists, so when the delegate of my alert view is called I still have my custom object around and the delegate method works fine.
Add the NSObject as strong property:
#import "Logout.h" // is NSObject
.
.
.
#property (nonatomic, strong) Logout *logout;
Then you will get the delegatemethods called in your NSObject.
Don´t forget to register the delegate for the UIAlertView:
#interface Logout () <UIAlertViewDelegate>
and in your method:
UIAlertView *a = [[UIAlertView alloc] initWithTitle:#"title"
message:#"message" delegate:self cancelButtonTitle:#"cancel"
otherButtonTitles:#"ok", nil];
[a show];
How To Present An Alert View Using UIAlertController When You Don't Have A View Controller. Detail description.
Yes, you can only use UIAlertController only in UIViewController classes. So how can we do it in NSObject classes. If you see the description link given above you will get to the answer. To summarise in a line for the above description: Create a new window above the the current window. This new window will be our viewController where we display alert. So using this viewController you can call the method [presentViewController: animated: completion:].
Answer:
dispatch_async(dispatch_get_main_queue(), ^{
UIWindow* window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.rootViewController = [UIViewController new];
window.windowLevel = UIWindowLevelAlert + 1;
NSString *msg=#“Your mssg";
UIAlertController* alertCtrl = [UIAlertController alertControllerWithTitle:#“Title" message:msg preferredStyle:UIAlertControllerStyleAlert];
[alertCtrl addAction:[UIAlertAction actionWithTitle:NSLocalizedString(#"Yes",#"Generic confirm") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
// do your stuff
// very important to hide the window afterwards.
window.hidden = YES;
}]];
UIAlertAction *cancelAction= [UIAlertAction actionWithTitle:#"cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
window.hidden = YES;
}];
[alertCtrl addAction:cancelAction];
//http://stackoverflow.com/questions/25260290/makekeywindow-vs-makekeyandvisible
[window makeKeyAndVisible]; //The makeKeyAndVisible message makes a window key, and moves it to be in front of any other windows on its level
[window.rootViewController presentViewController:alertCtrl animated:YES completion:nil];
});

How to create a method to return UIAlertViews?

I have a question about showing an alert on the screen. The thing is: I have an app with 20 to 30 different screens (nibs) And in each nib I do some checking to see if the user has inserted text in a textedit. And some alert messages are identical to others. Like In 3 nibs there is a text field for the user to enter his age, and the an alert that shows up if he left it blank.
What i want to do is to create a method to show these alerts so I don`t need to have the same alert on different nibs. instead of calling an alert view in each nib, I would call the method and pass what kind of alertview to pop up.
What would be the best way to implement this method?
TIA.
You can just alloc init a new UIAlertView as usual but you have to remember to pass the delegate in.
Here is my method:
- (UIAlertView *)getUIAlertViewWithDelegate:(id)delegate title:(NSString *)title cancelTitle:(NSString *)cancel {
return [[[UIAlertView alloc] initWithTitle:title delegate:delegate cancelButtonTitle:cancel otherButtonTitles:nil] autorelease];
}
All right, I managed to do it. Thanks to all that helped. This is my final solution
I created a common.m classs for some methods that I use in various nibs.
Common.h
#interface MetodosGerais : NSObject <UIAlertViewDelegate>{...}
- (void)getUIAlertViewWithDelegate:(id)delegate title:(NSString *)title cancelTitle:(NSString *)cancel;
Common.m
- (void)getUIAlertViewWithDelegate:(id)delegate title:(NSString *)title cancelTitle:(NSString *)cancel {
if (title == #"Enter your Height"){
[[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Atention!", #"Atenção!")
message:#"You Should enter Your Height."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil] autorelease] show];
}
else if (title == #"Enter your Age"){
[[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Atention!", #"Atenção!")
message:#"You Should enter your Age."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil] autorelease] show];
}
...
and in my classes that I want to use it I did
Common *myAlert = (Common *)[[UIApplication sharedApplication] delegate];
if ([idade.text length] == 0) {
[myAlert getUIAlertViewWithDelegate:self title:#"Enter Your Age" cancelTitle:#"OK"];
}...

How to control other buttons' actions?

Hi so say I have this code:
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle: #"Yay"
message: msg
delegate: self
cancelButtonTitle: #"Proceed..."
otherButtonTitles: #"1", #"2", nil];
How do I control the other buttons "1" and "2"? (Suppose all other necessary code is in place, such as the undefined variable msg)
Thanks!
You need to set the delegate property of your UIAlertView instance (i.e. alert) and implement the method alertView:clickedButtonAtIndex:.
Let's assume that you want the current class to be the delegate of the UIAlertView. You would use the following line of code:
alert.delegate = self;
Next, you would setup the current class to implement the UIAlertViewDelegate protocol in the .h file. For example:
#interface MyClass : NSObject <UIAlertViewDelegate>
Then you would simply implement the alertView:clickedButtonAtIndex: method in the .m file:
- alertView:a clickedButtonAtIndex:i {
NSLog(#"You clicked %d.", i);
}