How can i set the delegate method of a UIWebView in a class?
when i do it, the app carsh.
#interface MineWebViewHandle : NSObject<UIWebViewDelegate>
#end
//.m
#implementation MineWebViewHandle
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *urlString = [[request URL] absoluteString];
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
NSLog(#"did start load");
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSLog(#"did finished ");
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
NSLog(#"webview error:%#",[error localizedDescription]);
}
i use it:
self.m_pWebView = [[UIWebView alloc] initWithFrame:CGRectZero];
MineWebViewHandle *handle = [[MineWebViewHandle alloc]init];
self.m_pWebView.delegate = handle;
self.m_pWebView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.m_pWebView];
how do i use it rightly?
Your delegate look like is create in viewDidLoad,and in the end of this it´s release (put to nil).
You need create a property called: MineWebViewHandle *handle.
in your viewController.h add:
#property (nonatomic,strong)MineWebViewHandle *handle;
and change in your code:
self.handle = [[MineWebViewHandle alloc]init];
self.m_pWebView.delegate = self. handle;
And it´s good idea your webView will have dimensions, change this:
self.m_pWebView = [[UIWebView alloc] initWithFrame:self.view.frame];
Webview is the UIView, Not a controler.
Please remove the NSObject and add the UIview
#interface MineWebViewHandle : UIView
#end
Related
My cordova library was old and had to update it because of iOS11. I have updated it to latest version 4.4.0. CDVViewController is totally different now which enables webviewEngine that takes UIWebview or WKWebview. How do I initiate Cordova webview in my native app. Previously, I loaded it like this -
- (void) viewDidLoad {
[super viewDidLoad];
CDVViewController *viewController = [CDVViewController new];
viewController.wwwFolderName = #"www";
viewController.startPage = #"blank.html";
viewController.view.frame = self.webView.frame;
[self.webView removeFromSuperview];
self.webView = nil;
[self.view addSubview:viewController.view];
self.webView = viewController.webView;
viewController.webView.delegate = self;
self.cordovaWebViewController = viewController;
self.webView.scalesPageToFit = YES;
if (self.url)
[self.webView loadRequest: [NSURLRequest requestWithURL: self.url]];
}
And used webview delegates as
#pragma mark Webview Delegate
- (void) webViewDidStartLoad: (UIWebView *) webViewLocal {
[self.spinner startAnimating];
[self.cordovaWebViewController webViewDidStartLoad:webViewLocal];
}
- (void) webViewDidFinishLoad: (UIWebView *) webViewLocal {
[self.spinner stopAnimating];
[self.cordovaWebViewController webViewDidFinishLoad:webViewLocal];
}
- (void) webView: (UIWebView *) webView didFailLoadWithError: (NSError *) error {
[self.spinner stopAnimating];
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
if ([request.URL.absoluteString hasSuffix: #"link_restart_movie_file"]) {
[self loadItem];
return NO;
}
[self.webView loadRequest: request];
return NO;
}
[self.cordovaWebViewController webView:self.webView shouldStartLoadWithRequest:request navigationType:navigationType];
return YES;
}
How do I use WebviewEngine to load in my viewcontroller? Is there any tutorial, that explains the usage of CDVViewController? Please help! Thanks!
I have developed the webView in method viewDidLoad in ViewController
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
id <UIWebViewDelegate> delegate =[[MyDelegate alloc] init];
webView.delegate = delegate;
NSError *error;
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"];
NSString *htmlContent = [[NSString alloc] initWithContentsOfFile:htmlFile encoding:NSUTF8StringEncoding error:&error];
[webView loadHTMLString:htmlContent baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
[self.view addSubview:webView];
I set the delegate on instance of class MyDelegate.
In MyDelegate Class:
#import <UIKit/UIKit.h>
#interface MyDelegate : NSObject <UIWebViewDelegate>
#end
#import "MyDelegate.h"
#implementation MyDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
return YES;
}
#end
But app my crash during start the loasding.
If I not load html content, but url ('google.com' for example) crash happens.
When I comment this 'webView.delegate = delegate;' crash doesn't happens.
I know that I can use this in ViewController.h:
#interface ViewController : UIViewController<UIWebViewDelegate>
and this in viewDidLoad:
webView.delegate = self;
but I need use other class as delegate (not ViewController), but webview must be located in ViewController.
How I can make this?
Help me!
Let me point to the root cause.
UIWebView delegate attribute is a weak reference ("unowned(unsafe)" in Swift source code), which means its memory can be freed at any time.
So to solve this, you have to keep a reference into your controller as a class attribute.
Example of solution tested successfully in Swift:
class MyUIViewController : UIViewController{
let leftDelegate:MyWebViewDelegate = MyWebViewDelegate()
...
}
- (void)viewDidLoad {
[super viewDidLoad];
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
id<UIWebView> delegate =(id)self;
webView.delegate = delegate;
NSError *error;
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"];
NSString *htmlContent= [NSString stringWithContentsOfFile:htmlFile encoding:NSUTF8StringEncoding error:&error];
[webView loadHTMLString:htmlContent baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
[self.view addSubview:webView];
}
MyDelegate.h
#import <UIKit/UIKit.h>
#protocol UIWebView <UIWebViewDelegate>
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
#end
#interface MyDelegate : NSObject
#end
In your ViewController itself you can implement UIWebViewDelegate.
-(void)viewDidLoad
{
[super viewDidLoad];
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
webView.delegate = self;
NSError *error;
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:#"index" ofType:#"html"];
NSString *htmlContent= [NSString stringWithContentsOfFile:htmlFile encoding:NSUTF8StringEncoding error:&error];
[webView loadHTMLString:htmlContent baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
[self.view addSubview:webView];
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
return YES;
}
I'm posting a separate answer because I think there's a need to provide explicit Objective-C solution, since OP might miss David GONZALEZ's answer because it's in Swift, although he absolutely nailed it.
The problem is that by the time web view calls its delegate, that delegate has been deallocated. So the solution would be to add property of MyDelegate type to ViewController private extension declaration:
#interface ViewController ()
#property (nonatomic, strong) MyDelegate* webViewDelegate;
#end
And then to store MyDelegate instance created in -viewDidLoadin that property:
...
id <UIWebViewDelegate> delegate =[[MyDelegate alloc] init];
webView.delegate = delegate;
self. webViewDelegate = delegate;
...
I am using the below to add a ProgressView while the UIWebView is loading and dismissing it when it's done.
The problem is the UIWebView is started twice and also the ProgressView, So when the page is done loading only one ProgressView is dismissed and the other one doesn't.
ViewController.h
#import <UIKit/UIKit.h>
#interface DirectionViewController : UIViewController <UIWebViewDelegate>
#property (weak, nonatomic) IBOutlet UIWebView *directionWebView;
#end
ViewController.m
#interface ViewController ()
#property UIColor *yellowColor;
#property MRProgressOverlayView *progressView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.yellowColor =
[UIColor colorWithRed:244.0f/255.0f
green:208.0f/255.0f
blue:63.0f/255.0f
alpha:1.0f];
self.directionWebView.delegate = self;
NSString *urlMap = [NSString stringWithFormat:#"%#%f,%f&zoom=14", #"http://maps.google.com/maps?q=", self.coordLat, self.coordLong];
NSURL *url = [NSURL URLWithString:urlMap];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[self.directionWebView loadRequest:urlRequest];
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"WebView start loading...");
self.progressView = [MRProgressOverlayView new];
self.progressView.mode = MRProgressOverlayViewModeIndeterminateSmall;
[self.view.window addSubview:self.progressView];
[self.progressView setTintColor:self.yellowColor];
[self.progressView setTitleLabelText:#"Loading ..."];
[self.progressView show:YES];
return YES;
}
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
[self.progressView dismiss:YES];
}
#end
Dont know why it is calling twice but you can do this.
if(!self.progressView){
self.progressView = [MRProgressOverlayView new];
self.progressView.mode = MRProgressOverlayViewModeIndeterminateSmall;
[self.view.window addSubview:self.progressView];
[self.progressView setTintColor:self.yellowColor];
[self.progressView setTitleLabelText:#"Loading ..."];
[self.progressView show:YES];
}
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
[self.progressView dismiss:YES];
self.progressView = nil;
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
it may be triggered more than once, e.g redirects, iframe loading and so on. It's very inconvenient to initialise objects here.
Use Lazy Initialization design pattern for it
- (MRProgressOverlayView *)progressView {
if (_progressView) {
_progressView = [... alloc] init];
// ... setup your progressView
}
return _progressView;
}
btw, you forgot about error handling:
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
// You need to stop loading indicator here, right?!
}
Second thing is - (maybe for you case it's not a big deal) but usually it's tricky to exactly determine when UIWebView stopped loading (all the resources loaded, AJAX requests and so on)
Personally I'm using something like code below to manage activity indicator start/stop behaviour for UIWebView
#pragma mark UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"Webview (%p) starting to load URL: %#", self, request.URL);
self.URL = request.URL;
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
[self.spinner startAnimating];
self.webViewLoadingCount++;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
self.webViewLoadingCount--;
if (self.webViewLoadingCount > 0) return;
[self.spinner stopAnimating];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
self.webViewLoadingCount--;
[self.spinner stopAnimating];
// Ignore NSURLErrorDomain error (-999).
if (error.code == NSURLErrorCancelled) return;
// Ignore "Frame Load Interrupted" errors
if (error.code == 102 && [error.domain isEqual:#"WebKitErrorDomain"]) return;
NSLog(#"WebView (%p) experienced an error: %#.", self, [error localizedDescription]);
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
This delegate method can be called multiple times (in case of redirection for example). So I would recommend to move your Progress view init code to viewDidLoad method.
- (void)viewDidLoad {
[super viewDidLoad];
//...
self.progressView = [MRProgressOverlayView new];
self.progressView.mode = MRProgressOverlayViewModeIndeterminateSmall;
[self.view.window addSubview:self.progressView];
[self.progressView setTintColor:self.yellowColor];
[self.progressView setTitleLabelText:#"Loading ..."];
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"WebView start loading...");
[self.progressView show:YES];
return YES;
}
-(void)webViewDidFinishLoad:(UIWebView *)webView {
[self.progressView dismiss:YES];
}
I'm working with a UIWebView and am already using webViewDidFinishLoad: method with an optional block that gets executed after loading complete:
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
[super webViewDidFinishLoad:webView];
//... bunch of other code
if(self.webViewFinishLoadBlock != nil)
{
self.webViewFinishLoadBlock();
}
}
Now I'm working with an even more complicated sequence of loading pages and redirects that makes the logic above not sufficient. I don't want to register myself as a delegate of dummyWebView and have to juggle multiple completion blocks stored in my view controller's properties:
dummyWebView = [[UIWebView alloc] initWithFrame:CGRectZero];
[dummyWebView loadRequest:[NSURLRequest requestWithURL:logoutURL]];
//Ideally here I would know when dummyWebView finishes loading, because there's some code I want to execute once it is done
My question is:
Is there some kind of third party framework that would allow me to use loadRequest:withCompletion: to simplify writing callback code?
You can just:
Subclass UIWebView with a property to hold the webViewDidFinish completion block;
Make sure it specifies its delegate;
Implement the webViewDidFinish much like you wrote it (though I'd suggest the block return both the web view as well as the NSError object, if any); and
Implement the webView:didFailLoadWithError:, too.
Thus:
// MyWebView.h
#import <UIKit/UIKit.h>
typedef void(^WebViewFinishLoadBlock)(UIWebView *, NSError *);
#interface MyWebView : UIWebView
#property(nonatomic, copy) WebViewFinishLoadBlock webViewFinishLoadBlock;
- (void)loadRequest:(NSURLRequest *)request withCompletionHandler:(WebViewFinishLoadBlock)completionHandler;
#end
And
// MyWebView.m
#import "MyWebView.h"
#interface MyWebView () <UIWebViewDelegate>
#end
#implementation MyWebView
- (void)loadRequest:(NSURLRequest *)request withCompletionHandler:(WebViewFinishLoadBlock)completionHandler
{
self.delegate = self;
self.webViewFinishLoadBlock = completionHandler;
[self loadRequest:request];
}
#pragma mark - UIWebViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if (self.webViewFinishLoadBlock) {
self.webViewFinishLoadBlock(webView, nil);
self.webViewFinishLoadBlock = nil;
}
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
if (self.webViewFinishLoadBlock) {
self.webViewFinishLoadBlock(webView, error);
self.webViewFinishLoadBlock = nil;
}
}
#end
And then:
MyWebView *webView = [[MyWebView alloc] init];
webView.frame = self.view.bounds;
[self.view addSubview:webView];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request withCompletionHandler:^(UIWebView *webView, NSError *error) {
if (error) {
NSLog(#"failed: %#", error);
} else {
NSLog(#"succeeded");
}
}];
- (void)webViewDidFinishLoad:(UIWebView *)webView
is a delegate method. By convention delegate methods require the object pass itself back to the delegate:
(UIWebView*)webView
Through a parameter.
If we want to get last request parameter using property request: that means webView.request.URL
The parent object can be the delegate for multiple objects, and it can identify which it is getting a response from though that parameter. Either switch on what responds to you or build some infrastructure to handle it more elegantly.
I want to open links of uiwebview in to the safari browser my code is working perfectly if I implement shouldStartLoadWithRequest method in viewController but when I implement shouldStartLoadWithRequest in same class and set UIWebView's delegate to self it doesn't work it get halt in between and shows assembly level code with error EXC_BAD_ACCESS(code=2, address=0x9) my files are as follows
//content of ShowView.h file
#import <UIKit/UIKit.h>
#interface ShowView : UIView <UIWebViewDelegate> {
}
- (void) showViewFunction;
#property (nonatomic, assign) UIViewController *mainViewContObj;
#end
//content of ShowView.m file is :
#import "ShowView.h"
#implementation ShowView
- (void) showViewFunction {
UIWebView *aWebView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 150)];
aWebView.autoresizesSubviews = YES;
aWebView.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
[aWebView setDelegate:self];
NSString *urlAddress = #"http://localhost/test/index.php";
NSURL *url = [NSURL URLWithString:urlAddress];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
aWebView.delegate = self;
[aWebView loadRequest:requestObj];
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
[[[self mainViewContObj] view] addSubview:aWebView];
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"In shouldStartLoadWithRequest method");
if ([[[request URL] absoluteString] isEqual:#"http://localhost/test/index.php"])
return YES;
[[UIApplication sharedApplication] openURL:[request URL]];
return NO;
}
#end
// Content of ViewController.h
#import "ViewController.h"
#import "ShowView.h"
#interface mnetViewController ()
#end
#implementation mnetViewController
- (void)viewDidLoad {
[super viewDidLoad];
MNETMobAd *bannerObj = [[MNETMobAd alloc] init];
bannerObj.mainViewContObj = self;
[bannerObj showAd];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Some times html page shows but when I click on link it opens in same UIWebview window, and not even going into the shouldStartLoadWithRequest method, am I doing anything wrong?
Your code isn't clear, might need more info's, but from what i see, the ShowView class is never instanciated, so it shouldn't even show.
you should make something like this i guess :
//mnetViewController.m
#import "mnetViewController.h"
#import "ShowView.h"
#interface mnetViewController ()
#end
#implementation mnetViewController
- (void)viewDidLoad {
[super viewDidLoad];
ShowView* theShowView = [[ShowView alloc] initWithFrame:CGRectMake(insert the frame you want your webview to have)];
theShowView.autoresizesSubviews = YES;
[self.view addSubview:theShowView];
[theShowView release];
MNETMobAd *bannerObj = [[MNETMobAd alloc] init];
bannerObj.mainViewContObj = self;
[bannerObj showAd];
}
now for the ShowView class, try something like this :
//ShowView.h
#import <UIKit/UIKit.h>
#interface ShowView : UIView <UIWebViewDelegate> {
}
#end
//ShowView.m
#import "ShowView.h"
#implementation ShowView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
UIWebView *aWebView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
aWebView.scalesPageToFit = YES;
[aWebView setDelegate:self];
[self addSubview:aWebView];
NSString *urlAddress = #"http://localhost/test/index.php";
NSURL *url = [NSURL URLWithString:urlAddress];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[aWebView loadRequest:requestObj];
[aWebView release];
}
return self;
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"In shouldStartLoadWithRequest method for URL : %#",[request [URL absolutString]]);
if ([[[request URL] absoluteString] isEqual:#"http://localhost/test/index.php"])
return YES;
[[UIApplication sharedApplication] openURL:[request URL]];
return NO;
}
This should work, i didn't try it, i'll comeback tomorrow to try it if necessary.