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];
}
Related
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];
}
}
I have this code, which gets called from another class like this:
// send message not to turn off machine and display network indicator
[cm displayAlert: #"Warning" andData: #"Database restore is about to begin; DO NOT turn iPad OFF during restore!" andTag:0 andViewController:self];
and this is the code:
#pragma mark - displayAlert
- (void) displayAlert: (NSString *)alertTitle andData: (NSString *) alertMessage andTag: (int) tag andViewController: vc {
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:alertTitle
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Cancel", #"Cancel action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
NSLog(#"Cancel action");
}];
UIAlertAction *okAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"OK", #"OK action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
NSLog(#"OK action");
}];
[alertController addAction:cancelAction];
[alertController addAction:okAction];
dispatch_async(dispatch_get_main_queue(), ^{
[vc presentViewController:alertController animated:YES completion:nil];
});
}
The problem is that the presentation is either delayed or not shown at all. I used the GCD code which many, if not all, of the examples on SO show... what could be causing this delay?
I've tried Several ways to fix this deprecated code but nothing helped me I'm new to Objective C and iOS please help me fix this...It's working fine in iOS8 but not in iOS9..
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[super alertView:alertView clickedButtonAtIndex:buttonIndex];
if (buttonIndex)
{
switch (alertView.tag)
{
case kAccessAddressBook:
{
[self displayFindFriendView:[NSNumber numberWithInteger: CS_CONTACTS ]];
}
break;
case kFindFriendEmail:
{
}
break;
case kLogout:
{
// Hit Logout API
[self userLogout];
}
break;
case kClearSearchHistory:
{
// Clear Search History Data base.
[[CSCoreDataHandler sharedInstance] deleteManagedObjectsInModel:#"CSRecentSearch"];
}
break;
default:
break;
}
}
}
-(void)alert
{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"My Alert"
message:#"This is an alert."
preferredStyle:UIAlertControllerStyleAlert];
if (buttonIndex)
{
switch (alertView.tag)
{
case kAccessAddressBook:
{
defaultAction = [UIAlertAction actionWithTitle:[self displayFindFriendView:[NSNumber numberWithInteger: CS_CONTACTS ]]; style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
}
break;
case kFindFriendEmail:
{
}
break;
case kLogout:
{
// Hit Logout API
[self userLogout];
}
break;
case kClearSearchHistory:
{
// Clear Search History Data base.
[[CSCoreDataHandler sharedInstance] deleteManagedObjectsInModel:#"CSRecentSearch"];
defaultAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
}
break;
default:
break;
}
}
[alert addAction:defaultAction];
[self presentViewController:alert animated:YES completion:nil];
}
AlertView is depricated in iOS 8.So we need to use UIAlertController.
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:#"Title"
message:#"Message"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *actionAccessAddressbook = [UIAlertAction
actionWithTitle:#"Access"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self displayFindFriendView:[NSNumber numberWithInteger: CS_CONTACTS ]];
}];
UIAlertAction *actionFindFriendEmail = [UIAlertAction
actionWithTitle:#"Find Friend Email"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
//...Do your stuff here
}];
UIAlertAction *actionLogout = [UIAlertAction
actionWithTitle:#"Logout"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self userLogout];
}];
UIAlertAction *actionClearSearchHistory = [UIAlertAction
actionWithTitle:#"ClearSearchHistory"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[[CSCoreDataHandler sharedInstance] deleteManagedObjectsInModel:#"CSRecentSearch"];
}];
[alert addAction:actionAccessAddressbook];
[alert addAction:actionFindFriendEmail];
[alert addAction:actionLogout];
[alert addAction:actionClearSearchHistory];
[self presentViewController:alert animated:YES completion:nil];
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];
}
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;
}