UIAlertController handler becomes nil - objective-c-blocks

I have an UIAlertController in my app, so i want show alert everytime menuButton pressed. I need to recreate _menuAlert for specific idea (It need to check object for another action,that are not described). So if i keep _shareAction, but recreate UIAlertController, somehow, it's handler becomes nil.
I feel missing something big about blocks to understand why this happens. Please, help me understand.
There is code:
- (UIAlertController *)menuAlert
{
_menuAlert = [UIAlertController alertControllerWithTitle:nil
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
#weakify(self);
if (!_shareAction)
{
_shareAction = [UIAlertAction actionWithTitle:NSLocalizedString(#"SHARE_BTN_TEXT", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
#strongify(self);
[self.navigationController presentShareVCWithList:_list];
}];
}
if (!_cancelAction) {
_cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString(#"ALERT_CANCEL", nil)
style:UIAlertActionStyleCancel
handler:nil];
}
[_menuAlert addAction:_shareAction];
[_menuAlert addAction:_cancelAction];
return _menuAlert;
}
How menuAlert called :
- (void)menuButtonPressed
{
[self presentViewController:self.menuAlert animated:YES completion:nil];
self.menuAlert.view.tintColor = COLOR_BACKGROUND_GREEN;
}

Related

Understanding decidePolicyForNavigationResponse with user interaction

I would like to get user approval before opening a link using WKWebView. (Objective C) I am using decidePolicyForNaviagationResponse.
When it encounters a link in the HTML it should ask using a UIAlertController if it is OK to follow the link or not (in the simplest implementation).
However it appears to be running asynchronously, so first it opens the link and then eventually gets around to popping up the alert.
How do I encounter the link, pop up the alert and THEN either open the link or not. I’m guessing either something about blocks that I don’t understand like a completion handler or perhaps using semaphores, although my modest attempts at them didn’t work.
I have simplified the code to make it clear what’s happening.
Thank you!
static bool launchPermission = false;
#property (strong, nonatomic) WKWebViewConfiguration *theConfiguration;
#property (strong, nonatomic) WKWebView *webView;
.
.
.
_webView.navigationDelegate = self;
[_webView loadRequest:nsrequest];
[self.view addSubview:_webView];
.
.
.
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
[self askPermissionForExternalLink];
if (launchPermission)
{
decisionHandler(WKNavigationResponsePolicyAllow);
}
else
{
decisionHandler(WKNavigationResponsePolicyCancel);
}
}
- (void) askPermissionForExternalLink
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Open external Web Conten?" message:#"Link is embedded with other content" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Cancel", #"Cancel action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
NSLog(#"Cancel action");
[self cancelMethod];
//return;
}];
UIAlertAction *okAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"OK", #"OK action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
NSLog(#"OK action");
//[self launchURL];
[self OKMethod];
}];
[alert addAction:cancelAction];
[alert addAction:okAction];
[alert show];
}
- (bool) cancelMethod
{
launchPermission = false;
return false;
}
- (bool) OKMethod
{
launchPermission = true;
return true;
}
You can try it in this way using blocks first in decidePolicyForNavigationResponse make decideAction inside the block
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
void (^ launchPermission)(BOOL) = ^(BOOL isAllow)
{
if(isAllow)
{
decisionHandler(WKNavigationActionPolicyAllow);
}
else
{
decisionHandler(WKNavigationActionPolicyCancel);
}
return;
};
[self askPermissionForExternalLink];
}
here based on user choice send YES or NO to launchPermission block
- (void) askPermissionForExternalLink
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Open external Web Conten?" message:#"Link is embedded with other content" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Cancel", #"Cancel action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
NSLog(#"Cancel action");
launchPermission(NO);
return;
}];
UIAlertAction *okAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"OK", #"OK action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
NSLog(#"OK action");
launchPermission(YES);
return ;
}];
[alert addAction:cancelAction];
[alert addAction:okAction];
[alert show];
}

UiAlertController disappears automatically, without user input

I am attempting to present an warning UIAlertAction when a user has not enter text into a particular field. When the field is missing text and the user presses send, the alert appears for a moment then dismisses.
#pragma mark - Actions
- (IBAction)sendPressed:(id)sender
{
if (_titleLabel.text.length == 0)
{
self.alert = [UIAlertController alertControllerWithTitle:#"Sorry"
message:#"Please enter a title, it is required"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
[self dismissViewControllerAnimated:YES completion:nil];
}];
[self.alert addAction:defaultAction];
[self presentViewController:self.alert animated:YES completion:nil];
}
else
{
}
if (_delegate && [_delegate respondsToSelector:#selector(reportControllerDidPressSend:)]) {
[_delegate reportControllerDidPressSend:self];
}
Are you aware, that you have a empty else branch and an independent second if statement? I am quite sure, you want an else if-state meant instead
Also the test if delegate is nil isn't needed.
#pragma mark - Actions
- (IBAction)sendPressed:(id)sender
{
if (_titleLabel.text.length == 0) {
self.alert = [UIAlertController alertControllerWithTitle:#"Sorry"
message:#"Please enter a title, it is required"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
[self dismissViewControllerAnimated:YES completion:nil];
}];
[self.alert addAction:defaultAction];
[self presentViewController:self.alert animated:YES completion:nil];
} else if([_delegate respondsToSelector:#selector(reportControllerDidPressSend:)])) {
[_delegate reportControllerDidPressSend:self];
}
}

iPad crashes when showing photos or taking photo--nil source view

The follow code crashes when run on an iPad with the error message:
(<UIPopoverPresentationController: 0x1377a19e0>) should have a non-nil
sourceView or barButtonItem set before the presentation occurs.'
However, in my code I have the following, and I checked with breakpoints and console prinouts to ensure sourceview and barbutton are not nil. What do I need to do to avoid this? The error only appears on an iPad. Here is the method that causes trouble:
- (void)showChooseImageOptions {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *chooseAction = [UIAlertAction actionWithTitle:#"Choose From Photos" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
[self showLibrary];
}];
UIAlertAction *takeAction = [UIAlertAction actionWithTitle:#"Take Photo" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
[self showCamera];
}];
self.directionsTextView.text=#"";
[alertController addAction:chooseAction];
[alertController addAction:takeAction];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
if (self.imageView.image) {
self.directionsTextView.text=#"Tap the circle to change the image";
} else{
self.directionsTextView.text=#"Tap the blue circle to choose or take an image";
}
}];
[alertController addAction:cancelAction];
alertController.view.tintColor=[ColorSuperclass returnApplicationMainColor];
alertController.popoverPresentationController.barButtonItem = self.navigationItem.rightBarButtonItem;
if ( [alertController respondsToSelector:#selector(popoverPresentationController)] ) {
// at least iOS8
alertController.popoverPresentationController.sourceView = self.view;
}
[self presentViewController:alertController animated:YES completion:nil];
}
And the method showCamera and showLibrary are nothing but:
-(void)showCamera {
if (TARGET_IPHONE_SIMULATOR) {
//do nothing, the simulator cannot handle pressing the take photos...
} else{
[self showImagePickerForSourceType:UIImagePickerControllerSourceTypeCamera];
}
}
- (void)showLibrary {
[self showImagePickerForSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
}
and the show source type method:
- (void)showImagePickerForSourceType:(UIImagePickerControllerSourceType)sourceType {
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.modalPresentationStyle = UIModalPresentationPopover; //this line is very important, because otherwise, the tab bar could go out of scope (a consequence of using modal segues and tab controllers!)
imagePickerController.sourceType = sourceType;
imagePickerController.delegate = self;
imagePickerController.allowsEditing = YES;
[self presentViewController:imagePickerController animated:YES completion:nil];
}

UIAlertController not allowing click

I am generating a UIAlertController for use in iOS8 to replace a UIActionSheet function.
The controller presents correctly, but I cannot click anywhere in the controller to get a response. I put in simple NSLog into the handler, and it is absolutely not firing.
Can anyone shed some light on this problem?
BTW, in iOS8 the UIActionSheet is ALSO not generating click events, but in iOS7 this works fine with absolutely no code change.
here is the code:
NSString *alertMessage = #"How do you wish to share ?";
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Share Image"
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];
// You can add as many actions as you want
UIAlertAction *emailShare = [UIAlertAction actionWithTitle:#"Share using email"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
NSLog(#"EMAIL");
}];
UIAlertAction *smShare = [UIAlertAction actionWithTitle:#"Share using twitter/facebook"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[self actionSheet:nil didDismissWithButtonIndex:1];
}];
UIAlertAction *cancelShare = [UIAlertAction actionWithTitle:#"cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[self actionSheet:nil didDismissWithButtonIndex:2];
}];
// Add actions to the controller so they will appear
[alertController addAction:emailShare];
[alertController addAction:smShare];
[alertController addAction:cancelShare];
// Finally present the action
[self presentViewController:alertController animated:YES completion:nil];
You have a few different issues with your code. I've commented on the changes that are necessary. Let me know if you have any questions.
// No need to create a NSString. Just set the message.
// NSString *alertMessage = #"How do you wish to share?";
// If you'd like a UIActionSheet use UIAlertControllerStyleActionSheet as the preferredStyle
// Opposed to UIAlertControllerStyleAlert, which presents a UIAlertView
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Share Image"
message:#"How do you wish to share?" // Message Set
preferredStyle:UIAlertControllerStyleActionSheet]; // Changed to show action sheet
UIAlertAction *emailShare = [UIAlertAction actionWithTitle:#"Share using email"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[alertController dismissViewControllerAnimated:YES completion:nil];
// Call your function here
// ie. [self emailShare];
NSLog(#"First Button");
}];
UIAlertAction *smShare = [UIAlertAction actionWithTitle:#"Share using twitter/facebook"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
// [self actionSheet:nil didDismissWithButtonIndex:1];
// Where did you create actionSheet?
// didDismissWithButtonIndex: is not neccessary
// Just simply dismiss the UIAlertController
[alertController dismissViewControllerAnimated:YES completion:nil];
// And then call your function here
// ie. [self twitterShare];
NSLog(#"Second Button");
}];
UIAlertAction *cancelShare = [UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel // Set style to cancel
handler:^(UIAlertAction *action) {
// [self actionSheet:nil didDismissWithButtonIndex:2];
// Where did you create actionSheet?
// didDismissWithButtonIndex: is not neccessary
// Just simply dismiss the UIAlertController
[alertController dismissViewControllerAnimated:YES completion:nil];
NSLog(#"Cancel Button");
}];
[alertController addAction:emailShare];
[alertController addAction:smShare];
[alertController addAction:cancelShare];
[self presentViewController:alertController animated:YES completion:nil];

How to get UIAlertAction (UIAlertController) response from block handler?

I have this code where I want to display a message with a YES or NO response using UIAlertAction; this is my code:
-(void) displayAlert: (NSString *)alertTitle andMessage: (NSString *)alertMessage andViewController: (UIViewController *) viewController andTag:(int)tag {
// create controller
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:alertTitle
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];
// add buttons
if(tag == 0) { // ok/cancel response
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Cancel", #"Cancel action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
}];
UIAlertAction *okAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"OK", #"OK action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
}];
[alertController addAction:cancelAction];
[alertController addAction:okAction];
}
else if(tag == 1) { // yes/no response
UIAlertAction *noAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"No", #"No action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
}];
UIAlertAction *yesAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Yes", #"Yes action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
}];
[alertController addAction:noAction];
[alertController addAction:yesAction];
}
// display controller
[viewController presentViewController: alertController animated:YES completion:nil];
}
My problem is that the block does not allow a return (according to docs). So how do I return the response to the calling method? (it's not inline, it's in a class of common methods because it's used often).
You can't have a return value from block. Instead you can do the following:
Add completion block parameter to your displayAlert method. And include into this block definition an argument that would indicate button pressed (yes/no or whatever)
Call this completion block from both block handlers that you already have and pass proper value into it.
This way your application code can define one completion block that will be executed in both cases (when user tapped Yes or No) and you can handle it there.