简体   繁体   中英

UIWebView loadRequest not firing in custom function

First, thanks to everyone that takes the time to answer questions. I have gotten so many quick answers to problems over the years from StackOverflow.

I'm new to Object C and iOS programming, but starting with what I think should be a super simple app. It receives a push notification (which works fine) and redirects to a webpage when it has figured out its appid.

The problem is that the while I can get my UIWebView to loadRequest in the viewDidLoad, the same code will not execute in another function.

Here's the code:

AppDelegate.m

//  UFAppDelegate.m
#import "UFAppDelegate.h"
#import "UFViewController.h"

@implementation UFAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Add registration for remote notifications
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
     (UIRemoteNotificationType)(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];   
    return YES;
}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {

    // ....

    NSString *urlString = @"http://www.google.com";
    NSURL *url = [NSURL URLWithString:urlString];
    NSLog(@"Navigating to URL: %@", url);
    UFViewController *theview = [[UFViewController alloc] init];
    [theview handleOpenURL:url];
}

@end

ViewController.h:

//  UFViewController.h
#import <UIKit/UIKit.h>

@interface UFViewController : UIViewController
- (void)handleOpenURL:(NSURL *)url;
@end

ViewController.m:

//  UFViewController.m
#import "UFViewController.h"

@interface UFViewController ()
@property (weak, nonatomic) IBOutlet UIWebView *UFHWebView;
- (void)handleOpenURL:(NSURL *)url;
@end

@implementation UFViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"UFViewController.viewDidLoad started");

    // This block that assigns the url and loads it works perfectly here... but not down below.        
    NSString *urlString = @"http://search.yahoo.com/";
    NSURL *url = [NSURL URLWithString:urlString];

    NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
    [_UFHWebView loadRequest:requestObj];

    NSLog(@"UFViewController.viewDidLoad completed");
}

- (void)handleOpenURL:(NSURL *)url
{
    NSLog(@"UFViewController.handleOpenURL started");
    NSLog(@"url = %@",url);

    // The below loadRequest does not load!
    NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
    [_UFHWebView loadRequest:requestObj];
    NSLog(@"UFViewController.handleOpenURL completed");
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    NSLog(@"Error - %@", error);        
}

- (BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    return  YES;
}

@end

When run as coded here, the yahoo page shows from the loadRequest in the didload, but the handleOpenURL one still does not fire. If I comment out the loadRequest out of viewDidLoad, a blank page shows up and the handleOpenURL still does not fire.

Here's the debug sequence. viewDidLoad fires and completes before receiving the AppID and manually firing handleOpenURL:

2013-12-12 15:28:32.606 UF[5896:60b] UFViewController.viewDidLoad started
2013-12-12 15:28:32.608 UF[5896:60b] UFViewController.viewDidLoad completed
2013-12-12 15:28:32.765 UF[5896:60b] Navigating to URL: http://www.google.com
2013-12-12 15:28:32.769 UF[5896:60b] UFViewController.handleOpenURL started
2013-12-12 15:28:32.773 UF[5896:60b] url = http://www.google.com
2013-12-12 15:28:32.775 UF[5896:60b] UFViewController.handleOpenURL completed

Any help appreciated!

Are you sure you have just one instance of UFViewController here?

-didRegisterForRemoteNotificationsWithDeviceToken initializes a new web view controller in a local variable, but I can't see how it's getting shown to the user - you aren't doing anything with the vc once it's created.

Here's what I think might be happening:

  1. App starts. You have an instance of UFViewController in a storyboard. It loads, its -viewDidLoad is called, the log statements print and the webview loads correctly.
  2. A little later, the -didRegisterForRemoteNotificationsWithDeviceToken: method is called. This instantiates a UFViewController , and calls the -handleOpenUrl method on it. The -handleOpenURL log methods fire. However, this controller has not been loaded into the view hierarchy, so its view is never instantiated, and its -viewDidLoad is never called. This means that the webview that the -loadRequest: is being called on is nil . In Objective-C, it is valid to send a message to nil - unlike most other languages, no error or exception will be thrown. Instead, absolutely nothing happens, hence no web load. The UFViewController in -handleOpenUrl is only referred to by a local variable within the method, so when the method completes, the last strong reference to it vanishes and it is released.

To check this is correct:

- NSLog(@"%@",[self description]); in your vc methods - this will log the address of the objects and tell you whether they are the same.

- NSLog the web controller's view, to check whether it's nil in handleOpenURL

To solve your problem, I suggest using an NSNotification posted by the AppDelegate and listened for by the UFViewController that is fully instantiated. So in the vc you'd do:

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(handleRegistration:) name:@"HandleRegistrationNotification" object:nil];

and then implement the handleRegistration method:

-(void)handleRegistration:(NSNotification*)notification
{
     //your handleOpenUrl code 
}

and also

-(void)dealloc
{
    [[NSNotificationCenter defaultCenter]removeObserver:self name:@"HandleRegistrationNotification" object:nil];
}

(this bit is important because otherwise you may get a crash when the vc is dealloc'd)

Then, in the -didRegisterForRemoteNotifications: method:

[[NSNotificationCenter defaultCenter]postNotificationName:@"HandleRegistrationNotification" object:nil];

If you need to pass any information through the notification (eg urls) you can use the object parameter - this is accessible in the method called on the vc via the object property of the NSNotification parameter.

Full docs on notifications in Objective-C can be found here .

Some minor points - it's generally a bad idea to call your view controllers things like theView rather than theViewController since other people (or you in 6 months time) will end up thinking they're UIView s. Also the webview property should not start with a capital letter- that's generally only used for class names.

This should be called, "How to call a storyboard viewcontroller method from the appdelegate". And the answer is:

in AppDelegate.m:

#import "UFViewController.h"
...
UFViewController *rootViewController = (UFViewController*)self.window.rootViewController;
[rootViewController handleOpenURL:url];

In my code snippets above, this means replacing these lines

UFViewController *theview = [[UFViewController alloc] init];
[theview handleOpenURL:url];

with the ones above. The difference is my original code instantiates a new UFViewController object into a local variable, while the correct code just gets a handle to the view controller already created in the storyboard and calls its method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM