I have a class named ManagerClass.
Manager Class has a function showUIAlertController:
- (UIAlertController*)showUIAlertController:(NSString *)title message:(NSString *)message actions:(NSArray<UIAlertAction*>* )actions
This function should show alert controller with the parameters received.
So far so good...
Now i would like to take these actions and edit them somehow. Something like:
UIAlertAction *action = actions.firstObject;
UIAlertAction *actionCopyWithAdditionalAction = [UIAlertAction actionWithTitle:action.title style:action.style handler:^(UIAlertAction * _Nonnull action) {
[action "performTheAction"]; //perform the original action
[ManagerClass doSomething];
}];
"performTheAction" does not exist - it is just for you to understand what i am trying to achieve.
Does anyone has an idea how this task can be achieved ?
Did not find a way to do that while looking at Apple's UIAlertAction API
https://developer.apple.com/reference/uikit/uialertaction
Do you mean to perform a method provided by your code. Then use:
[self performSelector:#selector(aMethod:)];
or when sending an object with:
[self performSelector:#selector(aMethod:)
withObject:(id)object];
Note, the self here is referencing to the same class, it could be somewhere else as well.
Edit *
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"My Alert"
message:#"This is an alert."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
NSLog(#"42.");
}];
[alert addAction:defaultAction];
[self presentViewController:alert animated:YES completion:nil];
The console logs out 42. Put instead every action do you need.
Why you want to call a second alert which only displays the first alert and execute some of your code? You can do that in the first alert, too.
//Create the UIAlertController
UIAlertController *theAlertController = [UIAlertController alertControllerWithTitle:#"Your Title" message:#"Your Message" preferredStyle:UIAlertControllerStyleAlert];
//Add an UIAlertAction which the user can click at
[theAlertController addAction:[UIAlertAction actionWithTitle:#"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
//Execute your own code
//[self myOwnCode];
//Close the AlertController after your code
[self dismissViewControllerAnimated:YES completion:nil];
}]];
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:theAlertController animated:YES completion:nil];
});
Hope i understand you right.
You could pass a alert action model instead of a UIAlertAction.
so your method would look something like this:
- (UIAlertController*)showUIAlertController:(NSString *)title message:(NSString *)message actions:(NSArray<MyActionModel*>* )actions
where MyActionModel is a class with 3 properties
#interface MyActionModel: NSObject {
#property NSString * title;
#property UIAlertActionStyle * style;
#property ((void)^(UIAlertAction *)) action;
}
Then you can create your UIAlertActions when you need them and also add in your manager callbacks.
P.S. Sorry if my Objective-C is not quite right, I'm a bit rusty.
Related
I am developing an app which has to be upside down.
I would like to show a dialog to the user, which he can accept (which shall result in a auto rotation) or decline.
How to display such a dialog using objective-c?
I need to check the portrait modes both in the info.plist as well as programatically in the supportedInterfaceOrientations. Right?
Thank you very much in advance for helping!
Use UIAlertController for this.
UIAlertController * alert=[UIAlertController
alertControllerWithTitle:#"Title" message:#"Message"preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* yesButton = [UIAlertAction
actionWithTitle:#"Yes, please"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
**//What we write here????????**
NSLog(#"you pressed Yes, please button");
// call method whatever u need
}];
UIAlertAction* noButton = [UIAlertAction
actionWithTitle:#"No, thanks"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
**//What we write here????????**
NSLog(#"you pressed No, thanks button");
// call method whatever u need
}];
[alert addAction:yesButton];
[alert addAction:noButton];
[self presentViewController:alert animated:YES completion:nil];`
UIAlertController is crashing with this error message:
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x7fb9107674d0>)
also with a warning is thrown trying to capture the textFields objectAtIndex.
Any ideas ?
Warning.. Capturing 'controller' strongly in this block is likely to lead to a retail cycle.
I also tried to create a #property (weak) reference the warning goes away but the app still crashes with this:
-(void)viewWillAppear:(BOOL)animated
{
// self.controller = [UIAlertController alloc];
UIAlertController* controller = [UIAlertController alertControllerWithTitle:#"Add Alergy To List" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
[controller addTextFieldWithConfigurationHandler:^(UITextField * nametextField) {
_nameTextField.text = [controller.textFields objectAtIndex:0].text;
}];
UIAlertAction *save = [UIAlertAction actionWithTitle:#"Save Data" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
[self save:nil];
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:#"Cancel Action" style:(UIAlertActionStyleCancel) handler:nil];
[controller addAction:save];
[controller addAction:cancel];
[self presentViewController:controller animated:YES completion:nil];
}
__weak __typeof(UIAlertController) weakController = controller;
__weak __typeof(UITextField) weakTextField = _nameTextField;
[controller addTextFieldWithConfigurationHandler:^(UITextField *nametextField){
// nameTextField is the textField being returned in the block
// if you want to use _nameTextField you need to make that weak too
nameTextField.text = [weakController.textFields objectAtIndex:0].text;
// or
weakTextField.text = [weakController.textFields objectAtIndex:0].text;
}];
Add
__weak __typeof(UIAlertController) weakController = controller;
before your addTextFieldWithConfigurationhandler:.
Then you should replace
_nameTextField.text = [controller.textFields objectAtIndex:0].text;
with
nameTextField.text = [weakController.textFields objectAtIndex:0].text;
Without the _.
_ is for internal access of properties. Here you have to update the field given in parameter
The fact is that you are in a block and if you want to get text from controller fields, you have to make a weak reference to it. The weak controller will prevent increase retain count.
You are trying to load alertcontroller before the viewcontroller is allocated this warning is because you are adding UIAlertController directly inside viewWillAppear
This method is called before the view controller's view is about to be added to a view hierarchy
Just give some time for view to get added to the view hierarchy.to do that use dispatch_after or some other delay functionality.
Since you are getting textfield input inside a block you can't use strong declaration,So you have to declare __weak typeof(UIAlertController) *weakcontroller = controller; before entering the block.Check apple developer site here for more understanding.
Overall modified code is given bekow:
-(void)viewWillAppear:(BOOL)animated{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIAlertController* controller = [UIAlertController alertControllerWithTitle:#"Add Alergy To List" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
__weak typeof(UIAlertController) *weakcontroller = controller;
[controller addTextFieldWithConfigurationHandler:^(UITextField * nametextField) {
nametextField.text = [weakcontroller.textFields objectAtIndex:0].text;
}];
UIAlertAction *save = [UIAlertAction actionWithTitle:#"Save Data" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
[self save:nil];
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:#"Cancel Action" style:(UIAlertActionStyleCancel) handler:nil];
[controller addAction:save];
[controller addAction:cancel];
[self presentViewController:controller animated:YES completion:nil];
});
}
It will work without any issues.
Here's the problem up front: I have a UIAlertController that has a textfield. I want to save the content of that textfield as an NSString when the user touches a "Confirm" button in the alert. When the Confirm action block is executed, however, the alert is nil (presumably already dismissed and deallocated at that point), and thus so is its textfield, meaning I cannot save the textfield's text.
I am using a series of UIAlertControllers to allow a user to create a passcode for my app, such that any time the app comes to the foreground, the user is prompted for the code before the app can be used.
I created a category of UIAlertController with several convenience methods that return preconfigured alerts that I need to use. Here's one of them:
+ (UIAlertController*)passcodeCreationAlertWithConfirmBehavior:(void(^)())confirmBlock andCancelBehavior:(void(^)())cancelBlock {
UIAlertController *passcodeCreationAlert = [UIAlertController alertControllerWithTitle:#"Enter a passcode"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[passcodeCreationAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.keyboardType = UIKeyboardTypeNumberPad;
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (cancelBlock) {
cancelBlock();
}
}];
UIAlertAction* confirmAction = [UIAlertAction actionWithTitle:#"Confirm"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (confirmBlock) {
confirmBlock();
}
}];
[passcodeCreationAlert addAction:cancelAction];
[passcodeCreationAlert addAction:confirmAction];
passcodeCreationAlert.preferredAction = confirmAction;
confirmAction.enabled = NO;
return passcodeCreationAlert;
}
This method returns a UIAlertController that allows the user to enter their desired passcode into a textfield. When I call this method in my view controller, I pass blocks as parameters which are used as the UIAlertAction handlers:
- (void)presentCreatePasscodeAlert {
UIAlertController *alert = [UIAlertController passcodeCreationAlertWithConfirmBehavior:^{
firstPasscode = alert.textFields[0].text;
[self presentConfirmPasscodeAlert];
} andCancelBehavior:^{
[self presentEnablePasscodeAlert];
}];
alert.textFields[0].delegate = self;
[self presentViewController:alert animated:YES completion:nil];
}
To reiterate the problem now that there is more context: When the action block is entered at the line:
firstPasscode = alert.textFields[0].text;
the alert is nil, and so is its textfield, meaning I cannot save the textfield's text.
In a separate project, however, I tried getting the same functionality without using the category and custom convenience methods, and that works as desired:
- (void) createPassword {
UIAlertController *createPasswordAlert = [UIAlertController alertControllerWithTitle:#"Enter a password"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
__weak ViewController *weakSelf = self;
[createPasswordAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.delegate = weakSelf;
textField.keyboardType = UIKeyboardTypeNumberPad;
}];
UIAlertAction* confirmAction = [UIAlertAction actionWithTitle:#"Confirm"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
self.password = createPasswordAlert.textFields[0].text;
[self confirmPassword];
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self promptPasswordCreation];
}];
[createPasswordAlert addAction:confirmAction];
[createPasswordAlert addAction:cancelAction];
confirmAction.enabled = NO;
[self presentViewController:createPasswordAlert animated:YES completion:nil];
}
Sure enough, in the above code the alert exists when the Confirm block is entered, and I can save the text just fine.
Have I done something screwy by passing blocks as parameters to my convenience methods?
One of your problems is that when you create the block and send it into the initializer you are referencing a nil object inside the block. Then the block is saved with that nil reference and passed as a block to your handler.
UIAlertController *alert = [UIAlertController passcodeCreationAlertWithConfirmBehavior:^{
firstPasscode = alert.textFields[0].text;
[self presentConfirmPasscodeAlert];
} andCancelBehavior:^{
[self presentEnablePasscodeAlert];
}];
In there alert is not initialized yet when you create that block and send it to be saved. An option to fix that would be to use a standard initializer and then categorize a method to add the block functions that you want. Another option might be to try to add the __block modifier to the alert object, although I don't think that will help.
I have something like the following code. The Action sheet runs doSomething OK when it appears for the first time (in a button IBAction), but when it appears the second time, nothing happens the Action sheet just disappear without calling do something. Any idea?
#implementation ...
- (void) setActions {
UIAlertAction *opt1 = [UIAlertAction actionWithTitle:#"Option 1" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self doSomething:#"opt1"];}];
UIAlertAction *opt2 = [UIAlertAction actionWithTitle:#"Option 2" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self doSomething:#"opt2"];}];
UIAlertAction *opt3 = ...
self.opt1 = opt1;
self.opt2 = opt2;
self.opt3 = opt3;
- (void) showActionSheet {
...
UIAlertController *selectAS = [UIAlertController alertControllerWithTitle:#"Select Options"
message:#"msg" preferredStyle:UIAlertControllerStyleActionSheet];
if (xyz) {
[selectAS addAction:self.opt1];
[selectAS addAction:self.opt2];
}
else{
[selectAS addAction:self.opt1];
[selectAS addAction:self.opt3];
}
[self presentViewController:selectqAS
animated:YES completion:nil];
}
- (void) doSomething: (NSString *) opt{
....
}
Glad we got you up and running. My guess is your methods are getting lost in translation. You have methods intertwining each other which can be causing the confusion, specifically with self.opt1. per my comment, now that iOS8 has introduced UIAlertController, it comes with completion handlers, you should plan accordingly to that: something like the following :
-(IBAction)showActionSheet {
UIAlertController *selectAS = [UIAlertController alertControllerWithTitle:#"Select Options" message:#"msg" preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *opt1 = [UIAlertAction actionWithTitle:#"Option 1" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
//Don't have to call another method, just put your action 1 code here. This is the power of completion handlers creating a more structured outline
}];
UIAlertAction *opt2 = [UIAlertAction actionWithTitle:#"Option 2" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
//Don't have to call another method, just put your action 2 code here. This is the power of completion handlers creating a more structured outline
}];
UIAlertAction *opt3 = ...
if (xyz) {
[selectAs addAction:opt1];
[selectAs addAction:opt2];
} else {
[selectAs addAction:opt1];
[selectAs addAction:opt3];
}
[self presentViewController:selectAs animated:YES completion:nil];
}
Much more cleaner and actually uses the UIAlertController for it's intended purposes, no other method calls needed.
I have an app that consists of several view controllers, in each view controllers I could load a UIAlertView.
What would be the best way of having a global alert view function? So I could effectively have a function bit like
[GlobalAlertVIew alertview : 2];
the 2 referring to what alert it is (bit like a enum).
I have tried adding an NSObject file and calling that, however on the return function
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
how do I get from this file in the NSObject to the original view controller?
Thanks
You either need to configure the delegate on the configured UIAlertView
UIAlertView *alertView = [GlobalAlertView alertView:2];
alertView.delegate = self;
or expose the delegate in the factory method
[GlobalAlertView alertView:2 withDelegate:self];
I don`t know if you still need, but I did like this:
+ (void)alert: (UIViewController *) view title: (NSString *) title message: (NSString *) message;
+ (void)alert: (UIViewController *) view title: (NSString *) title message: (NSString *) message {
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
[view presentViewController:alert animated:YES completion:nil];
}
You can add the following code to your constants header file and import this header at .pch file to be able to see this file at app ,
#define SHOW_ALERT(title,msg){ UIAlertController *noDataFoundAlert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];UIAlertAction *okAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action){}]; [noDataFoundAlert addAction:okAction]; [self presentViewController:noDataFoundAlert animated:YES completion:nil];}
And you can call this method at any class like the following call ,
SHOW_ALERT(#"Please select any section", #"Please select any section");
Nada Gamal