Will iPhones webViewDidFinishLoad Detect End of Playback" of Streamed Audio File - iphone

I am trying to detect the end of playback of a podcast stream. I am using a loadRequest:[NSURLRequest requestWithURL:location] to load the web page that contains the mp3 file being played back. After looking through the archives of questions, I thought that maybe the implementation of the <UIWebViewDelegate> protocol and specifically the method webViewDidFinishLoad might be the answer. However, after implementing it, I am not seeing this method get called when the audio file finishes. In fact I never see the method get called, leading me to believe I may not have implemented it correctly. Here is what I did:
In the header file I have:
#interface MyWebViewController : UIViewController<UIWebViewDelegate> {
NSURL *location; // holds the web address that will be loaded by this app
IBOutlet UIWebView *theWebView;
}
In the class implementation file I have:
- (void)viewDidLoad {
[super viewDidLoad];
theWebView.delegate = self;
[theWebView loadRequest:[NSURLRequest requestWithURL:location]];
}
- (void)webViewDidFinishLoad:(UIWebView *)theWebView {
NSLog(#"view did finish");
}
Is this proper solution for determining the end of playback of an audio file that is loaded into a web page? If not, what would be a better way to detect the end of playback? If the idea behind this solution is correct, what did I do wrong with this implementation?

I am not sure if this is the problem or not, but you should not shadow your instance variable theWebView within the delegate method. I don't know that changing theWebView to webView is going to solve your problem of the delegate being fired but that is where I would look first.
To answer your original question, this delegate gets called after the Webview response is received not at the end of a streaming connection. I am also looking into a solution for a hook into the completion of a stream.

Related

How to add Pop up Button in UIWebView

The deafult pop up is get opened when I long press the music play option in any url from UIWebView.
I Want to add one more button in the pop up..Is it possible to do it..
Like I want to add FETCH button.
And Can I make changes in the default pop up functioning which is OPEN and COPY. shown below
I come to know that by google -
First of all, you really can’t add additional menu items to the default ones of the standard contextual menu. But you can switch off the contextual menu using a certain CSS property. So the solution would be to switch off the default menu and implement your own from scratch. And to implement your own contextual menu, you have to first catch the tab-and-hold gesture, get the coordinates of the finger on the screen, translate these coordinates into the coordinate system of the web page and finally look for the HTML elements at this location.
NEW ANSWER:
Looks like this is what we want, here:
https://stackoverflow.com/a/3198220/700471
OLD ANSWER:
Okay, after some research, here's the deal:
What you describe in your question seems accurate:
First of all, you really can’t add additional menu items to the
default ones of the standard contextual menu. But you can switch off
the contextual menu using a certain CSS property. So the solution
would be to switch off the default menu and implement your own from
scratch. And to implement your own contextual menu, you have to first
catch the tab-and-hold gesture, get the coordinates of the finger on
the screen, translate these coordinates into the coordinate system of
the web page and finally look for the HTML elements at this location.
So you are planning on implementing your own popover controller with contextual menu--fine, I won't get into that at all, I will assume you know how to do that.
What your question seems to be is, how do you take a long-tap gesture in the UIWebView and transform it into the coordinates of the webpage to find the DOM element that was selected, and use that as a context from which to generate your popover menu.
What I found was this, specifically this with this line of code:
NSString *js = [NSString stringWithFormat:#"document.elementFromPoint(%f, %f).innerHTML", touchPoint.x, touchPoint.y];
That looks like the JS you would need to figure out what element had just been long-pressed, and of course you would need to do some figure-figure to see if it was a link and execute your context menu from there, but that's not something I've looked into.
Some further thoughts:
Probably the easiest course would be to attach a UILongPressGestureRecognizer to your UIWebView (this can be done easily in a nib) and make sure that the "Sent Action" points to an appropriate IBAction on your ViewController. (I suppose you could use the delegate outlet as well, but I have never needed to do that.)
In any case, you can use the locationOfTouch:inView: method of your gesture recognizer, and the view you will probably want to use will be the UIWebView's content view, which I believe you can get with something like myWebView.scrollView.subviews[0] (or the objectAtIndex: variation if you are not using the new array index subscripts).
Anyway, I think I have provided enough to answer your question.
EDIT:
Okay, so you are still having trouble with this, so I went and made a test project and got it to work. One thing that is slightly annoying about this is that WebKit somehow adds a "buffer" area around objects in the DOM, meaning that if you touch slightly next to a link it will still highlight, but when you use the JS command elementFromPoint it doesn't do that, so you kinda have to touch more carefully to trigger the popup using this method. But, it works.
I made a blank project with the "single view" template, and threw a UIWebView into the xib, pointed its delegate outlet to File's Owner. Then I put a UILongPressGestureRecognizer into the xib, attached to the UIWebView. I set its delegate as File's Owner, and set its selector outlet to the longPressDetected IBAction in File's Owner. I also unchecked "Canceled in View" in the recognizer's properties in Interface Builder.
Here is the code for the view controller.
Interface:
//
// WVTViewController.h
// WebViewTest
//
#import <UIKit/UIKit.h>
#interface WVTViewController : UIViewController <UIWebViewDelegate, UIGestureRecognizerDelegate>
#property (nonatomic, weak) IBOutlet UIWebView *myWebView;
#property (nonatomic) BOOL didFirstLoad;
- (IBAction)longPressDetected:(id)sender;
#end
Implementation:
//
// WVTViewController.m
// WebViewTest
//
#import "WVTViewController.h"
#interface WVTViewController ()
#end
#implementation WVTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Just load google.
NSURL *theURL = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:theURL];
[self.myWebView loadRequest:request];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if (!self.didFirstLoad) {
// Disable the default contextual menu.
[webView stringByEvaluatingJavaScriptFromString:#"document.body.style.webkitTouchCallout='none';"];
}
}
// Called by the gesture recognizer.
- (IBAction)longPressDetected:(UILongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan) {
NSLog(#"Long press detected.");
CGPoint webViewCoordinates = [sender locationInView:self.myWebView];
NSLog(#"WebView coordinates are: %#", NSStringFromCGPoint(webViewCoordinates));
// Find the DOM element
NSString *locatorString = [NSString stringWithFormat:#"document.elementFromPoint(%f, %f).innerHTML", webViewCoordinates.x, webViewCoordinates.y];
NSString *result = [self.myWebView stringByEvaluatingJavaScriptFromString: locatorString];
NSLog(#"Element Found: %#", result);
}
}
// Necessary or the gesture recognizer won't call the IBAction.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
#end
As I said, I tested the above code and it works fine. You can of course change up your JS and use something other than innerHTML, such as tagName or href or whatever you like. Multiple checks may be necessary for what you're trying to do, possibly with queued JS commands (which would be lame), unless you could JSON-stringify the DOM object, pass it back to the Objective-C, convert to native objects and perform your checks in that environment--but, I'm no JS pro and I'm not going to investigate that.
As a note, I was a bit surprised that the coordinates that worked for elementFromPoint were the touch coordinates within the UIWebView itself. I had rigged up a whole block of code that iterated through myWebView.scrollView.subviews and found the content view, then called locationOfTouch:inView: on that view. But I was getting funky behavior, so on a hunch I used the webview coordinates and it worked fine, even on a big webpage when I was scrolled off to the side and down. I suspect that some kind of Apple-programmed behavior inside the webview may translate those coordinates. Possibly the JS's coordinate system is altered based on the way the content view is moved around inside the scrollview--that makes the most sense to me.
The proper and accurate way to answer this question is below link
http://www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/
you can fire the event with something like this:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if( navigationType == UIWebViewNavigationTypeLinkClicked )
{
// Handle your URL click here (open the popup menu)
// ...
return NO;
}
return YES;
}
don't forget to delegate the UIWebView

YouTube In WebVIew - iPhone

This is very strange to me...
I have a simple WebView that loads and interacts with the user exactly like safari mobile (iPhone). Now when you visit m.youtube.com in safari, the url changes when you click on a link to something like this...
http://m.youtube.com/watch?gl=US&hl=en&client=mv-google&v=HX6SyoZ5kw8
The problem with this is I don't think that url is being used in my webview... What do I mean? The following code is used to load a url every time the user try's to click on a link, and it works, but I have a problem with Youtube...
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = request.URL;
NSString *urlString = url.absoluteString;
NSLog(#"%#",urlString);
VideoURLTextBox.text = urlString;
return YES;
}
When I first start up the webview it loads m.youtube.com and NSLogs() it into my console, but when I decide to click on a video it fails to NSLog() therefore I don't think a new url is being loaded, but when you load m.youtube.com in safari and click on a video you load a url like above, so why does this not NSLog() in my iPhone application?
What your probably seeing is that the youtube video is running javascript to play the video, you'd have to intercept the javascript callbacks to see that. Here's a link to the youtube video player api, detailing the callbacks, https://developers.google.com/youtube/js_api_reference#SubscribingEvents
If you just want to embed a youtube video in an iOS application then use this.
Using the mobile YouTube site isn't great, I had lots of problems with it in the past and ended up making a UIWebView category. I came across your question and decided to throw it on github :-)
https://github.com/enigmaticflare/UIWebView-YouTube--iOS-Category--ARC-compliant-code
UIWebView+YouTube iOS category to simplify loading youtube videos.
Instructions
1) Add these files to your project
UIWebView+YouTube.h
UIWebView+YouTube.m
2) Initiate UIWebView for example
#property (strong, nonatomic) UIWebView *webView;
#synthesize webView = _webView
_webView = [[UIWebView alloc] initWithFrame:CGRectMake(0,0,212,172];
//set the X,Y origin of CGRect to where you want the webView to appear.
_webView.delegate = self;
[_webView loadYouTubeVideoID:#"gR8dO7Cln6M"];
[self.view addSubView:_webView];
Good luck, hope this is what your looking for :)
Adam
m.youtube.com is one giant java web app, delegate methods won't get called, presumably because any loading is written directly to the DOM in the UIWebView. Ran into this problem at work, not sure of the fix but there may be a way to inject the source with a JS click handler.

Put a link on home screen of my app and connect it to WebView

In my app i want to put my website's url on home screen and on clicking on it i want it to be open as a WebView.
How should i go for this.
Thanks,
Previous commenter is incorrect. You can open any hyperlink either externally with Safari or internally with a UIWebView.
Add a UIWebViewController to your project. Then, instantiate an instance of a the UIWebViewController that will be shown inside your app--you'll do this by declaring a property & synthesizing it within your main view controller (which will need to be declared as a UIWebViewDelegate), such as:
#interface MyMainViewController: UIViewController <UIWebViewDelegate> {
// Your implementation code here
}
When a user taps the button (assuming you make it a button, rather than just a text hyperlink), you instruct your app to add the UIWebView to the view stack, loading the correct link. You'll want to either do this within a modal view or within a navigation stack so your users can get back out of the web view, of course.
In your MyMainViewController implementation file, something like this:
-(void) showWebView {
// NOTE: I have not tested this, just prototyping
// off the top of my head
UIWebView *myWebView = [[UIWebView alloc] init];
myWebView.delegate = self;
NSURL *homeUrl = [[NSURL alloc] initWithString:#"http://example.com"];
NSURLRequest *homeRequest = [NSURLRequest requestWithURL:homeURL];
[myWebView loadRequest:homeRequest];
[self.presentModalViewController: myWebView animated:YES];
// Don't forget to release objects when you're done
[myWebView release]; // etc.
}
Now, this is off the top of my head from what I know and have done. But I hope you get the general idea. I offer no warranty of any kind here, but do guarantee this is entirely possible with minimal headache. If you get stuck, check out the developer references for UIWebView. Apple's docs are top-notch & show great examples to get you up and running quickly.
Best.

Reused UIWebView showing previous loaded content for a brief second on iPhone

In one of my apps I reuse a webview. Each time the user enters a certain view on reload cached data to the webview using the method :-
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
and I wait for the callback call
- (void) webViewDidFinishLoad:(UIWebView *)webView.
In the mean time I hide the webview and show a 'loading' label.
Only when I receive webViewDidFinishLoad do I show the webview.
Many times what happens I see the previous data that was loaded to the webview for a brief second before the new data I loaded kicks in.
I already added a delay of 0.2 seconds before showing the webview but it didn't help.
Instead of solving this by adding more time to the delay does anyone know how to solve this issue or maybe clear old data from a webview without release and allocating it every time?
Thanks malaki1974, in my case I wasn't using a modal view.
When I sat with an Apple engineer on WWDC 2010 and asked him this question his answer was simply: "Don't reuse UIWebViews, that's not how they were ment to be used."
Since then I make sure to calls this set of lines before allocating a new UIWebView
[self.myWebView removeFromSuperview];
self.myWebView.delegate = nil;
[self.myWebView stopLoading];
[self.myWebView release];
That solved the issue.
Clear the contents of the webview before you try to load new content
[self loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"about:blank"]]];
First, the UIWebView renders it contents in a background thread. Even when you receive webViewDidFinishLoad: it might not be completely done. Specially if it is an ajax-intense page that comes from the network.
You say you are hiding the view. I wonder if that means that the webview delays its drawing completely. What you could try is to move the UIWebView offscreen or obscure it with another view. Maybe that will change it's drawing behaviour.
If you do not need an interactive UIWebView then you can also consider to do it completely offscreen in a separate UIWindow and then create an image from that UIWebView's layer.
That's what I do, and it works:
[_webView stringByEvaluatingJavaScriptFromString:#"document.open();document.close();"];
Try loading a local file that is blank or has a loading graphic when you hide it, rather than just loading new content when you show it. Since the file is local it will be quick and even if the new page takes a while to load it will have either blank or loading expected behavior.
If you got controll over the html. You can communicate back to objective-c when the document is ready. Like so in jQuery:
function messageNative (name, string) {
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "appscheme://" + name + "/" + string);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
$(function() {
messageNative('webview', 'ready');
});
And then in UIWebView's delegate method webView:shouldStartLoadWithRequest:navigationType: wait for the request with url equal to "appscheme://webview/ready". Then you should know: the document is loaded and ready for display. Then all that is missing is a simple fade-in or something like that :)

UIWebView didFinishLoading fires multiple times

I have some code that needs to run after the a UIWebView finishes loading a document. For that I've set the UIWebView's delegate to my controller, and implemented the webViewDidFinishLoading method.
This gets called multiple times, depending on the type of page to load. I'm not sure if it's because of ajax requests, requests for images, or maybe even iframes.
Is there a way to tell that the main request has finished, meaning the HTML is completely loaded?
Or perhaps delay my code from firing until all of those events are done firing?
You can do something like this to check when loading is finished. Because you can have a lot of content on the same page you need it.
- (void)webViewDidFinishLoad:(UIWebView *)webview {
if (webview.isLoading)
return;
// do some work
}
It could be enlightening (if you haven't gone this far yet) to NSLog a trace of load starts and finishes.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"Loading: %#", [request URL]);
return YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(#"didFinish: %#; stillLoading: %#", [[webView request]URL],
(webView.loading?#"YES":#"NO"));
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"didFail: %#; stillLoading: %#", [[webView request]URL],
(webView.loading?#"YES":#"NO"));
}
I just watched the calls to all three in one of my projects which loads a help page from my bundle and contains embedded resources (external css, YUI!, images). The only request that comes through is the initial page load, shouldStartLoadWithRequest isn't called for any of the dependencies. So it is curious why your didFinishLoad is called multiple times.
Perhaps what you're seeing is due to redirects, or as mentioned, ajax calls within a loaded page. But you at least should be able balance calls to shouldStartLoad and either of the other two delegate functions and be able to determine when the loading is finished.
Check this one it so simply and easy way to achieve no need to write too much code:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if ([[webView stringByEvaluatingJavaScriptFromString:#"document.readyState"] isEqualToString:#"complete"]) {
// UIWebView object has fully loaded.
}
}
This question is already solved, but I see it lacks an answer that actually explains why multiple calls to webViewDidFinishLoad are actually expected behavior
The aforementioned method is called every time the webview finishes loading a frame. From the UIWebViewDelegate protocol documentation:
webViewDidFinishLoad:
Sent after a web view finishes loading a frame.
In fact, this is also true for all the other methods that comprise the UIWebViewDelegate protocol.
Try this it will work fine
-(void)webViewDidFinishLoad:(UIWebView *)webview
{
if (webview.isLoading)
return;
else
{
// Use the code here which ever you need to run after webview loaded
}
}
This happens because the callback method is called every time a frame is done loading. In order to prevent this set the "suppressesIncrementalRendering" property of the webview to true. this will prevent the webview from rendering until the entire data is loaded into the memory. This did the trick for me
I have notice something similar and it was a confusion: I have a UITabBarController, it seems to preload all ViewControllers linked to its tabs on launching the App (in spite of showing just first_Tab_ViewController), so when several tabs have ViewController with WebView their respective webViewDidFinishLoad are called and if I have copied pasted:
NSLog(#"size width %0.0f height %0.0f", fitingSize.width, fittingSize.height);
in several, I get several output in console that appears to be a double calling when they really are single calling in two different UIWebViews.
You could check the loading and request properties in the webViewDidFinishLoad method
Possibly related to this issue is a property on UIWebView introduced in iOS6: suppressesIncrementalRendering.