WinX Blog

Just another WordPress weblog

In the iPhone SDK, web views are accomplished using UIKit’s UIWebView class. Instead of allowing you direct access to WebKit’s WebView class, UIWebView handles everything for you and allows some control of the view. One thing Apple left out in UIWebView, for reasons unknown, is progress. If you want to have a progress bar in your web view so people know that something’s actually happening (in addition to the web activity indicator in the status bar), this is basically the only way to do it.

NOTE: This method DOES use what Apple considers to be a private framework. That said, there are a few applications in the App Store using this, and this method is slightly unlikely to change, because WebView is not an iPhone-specific thing, and if Apple changed it, tons of Mac applications would no longer work as well.

NOTE 2: In this post, I will talk about two classes. UIWebView, and WebView. These are NOT the same class. UIWebView is the public, official web view, and WebView is the private, internal WebKit web view. WebView has the estimated progress, but we want to use UIWebView as the main view so we don’t have to reinvent the wheel in a lot of ways.

So, say you have a UIWebView called officialSDKWebView. We want to extract the main WebView from that to get the progress. To do so, we have to use another internal class called UIWebDocumentView. From there, we can get the WebView easily. Here’s the code:


UIWebDocumentView *documentView = [officialSDKWebView _documentView];
WebView *coreWebView = [documentView webView];

Well, that was pretty easy, huh? But let’s make it easier, and do it in one line:

WebView *coreWebView = [[officialSDKWebView _documentView] webView];

Of course, the compiler won’t like this, as we don’t have header files… I uploaded the necessary header files to reduce the error count to zero (as well as some useless stuff), and you can download it here. Add these with

#import "WebView.h"
#import "UIWebDocumentView.h"
in your header file.
But wait, the compiler still isn’t happy… So we need to add two frameworks to the project. You can do this by right-clicking Frameworks in the left pane of Xcode, and choosing Add -> Existing Frameworks. Navigate to /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.2.sdk/System/
Library/PrivateFrameworks/
and then add WebKit.framework and WebCore.framework.
Of course, this is completely useless if we can’t figure out when the progress has changed, but we can fix that as well.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(progressEstimateChanged:) name:WebViewProgressEstimateChangedNotification object:coreWebView];

We’re now subscribed to progress change events, and it will run the method progressEstimateChanged: as soon as there is an update. So, let’s write a progressEstimateChanged method.


- (void)progressEstimateChanged:(NSNotification*)theNotification {
	// You can get the progress as a float with
	// [[theNotification object] estimatedProgress], and then you
	// can set that to a UIProgressView if you'd like.
	// theProgressView is just an example of what you could do.

	[theProgressView setProgress:[[theNotification object] estimatedProgress]];
	if ((int)[[theNotification object] estimatedProgress] == 1) {
		theProgressView.hidden = TRUE;
		// Hide the progress view. This is optional, but depending on where
		// you put it, this may be a good idea.
		// If you wanted to do this, you'd
		// have to set theProgressView to visible in your
		// webViewDidStartLoad delegate method,
		// see Apple's UIWebView documentation.
	}
}

And, there we are! That should do it. If you have any questions, or if I made a mistake in my code, feel free to make a comment. If you need an example, I’ll be posting an Xcode project probably at some point.

22 Comments

  1. Raj on February 27, 2009 7:12 am
  2. Alex Curylo on February 27, 2009 1:06 pm

    Thanks a whack, dude. I needed to sort out how to call -setCustomUserAgent: on the phone, and this appears to do the trick nicely!

  3. yile on February 27, 2009 1:06 pm

    thx, man!
    I just wanna try them out~!

  4. UIWebView internals at Under The Bridge on February 27, 2009 1:28 pm

    [...] with the strictest adherence possible to the rules, here’s a fellow who’s worked out how to get at the internal WebView, for purposes of presenting a progress bar: So, say you have a UIWebView called officialSDKWebView. [...]

  5. Ivan on March 2, 2009 3:59 am

    Thanks! Good stuff!

  6. Dirk on March 22, 2009 7:26 am

    Hi there,
    I am trying to get your code up and running right now…
    Can you please tell me where the notification name WebViewProgressEstimateChangedNotification is declared ?

    I still get the error “WebViewProgressEstimateChangedNotification” undeclared!

    I am using WebKit.framework and WebCore.framework of the 2.2.1 iPhone SDK version be be found in
    /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.2.1.sdk/System/Library/PrivateFrameworks/

    Thanks for good work !

    Dirk

    BTW: Will Apple still certify an application making use of this private framework ?

  7. Tung Do on March 24, 2009 4:38 am

    I have the same problem with “WebViewProgressEstimateChangedNotification” undeclared. Can’t find out how to fix it. :(

  8. Steven on March 25, 2009 10:11 am

    If you replace the line
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(progressEstimateChanged:) name:WebViewProgressEstimateChangedNotification object:coreWebView];
    with
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(progressEstimateChanged:) name:@”WebProgressEstimateChangedNotification” object:coreWebView];
    this seems to fix the problem.

  9. srw on March 28, 2009 3:52 pm

    Good stuff, what’s is interesting is that a UITextView is also embedding a UIWebView.

  10. Dirk on March 31, 2009 3:53 am

    @”WebProgressEstimateChangedNotification”
    This indeed helps, thanks!
    But again: Will Apple certify (and then sell using the AppStore) an application making use of this private Framework?

  11. Dirk on April 1, 2009 4:53 am

    >Good stuff, what’s is interesting is that a UITextView
    >is also embedding a UIWebView.
    What exactly do you mean by this? Why do you want incorporate a WebView inside a TextView ? What for? Please explain!

  12. mitschi on April 6, 2009 6:47 am

    apple does not allow the use of this:

    > “3.3.1 Applications may only use Published APIs in the manner
    > prescribed by Apple and must not use or call any unpublished or private
    > APIs. ”
    >
    > The non-public API that is included in your application comes from the
    > following private Apple frameworks – WebKit.framework.

  13. Darcy on April 11, 2009 9:53 pm

    I’ve tried everything and I can’t get this to work.
    No errors, it’s just the progress bar doesn’t change and the “progressEstimateChanged” method is never called.

  14. JaneRadriges on June 13, 2009 12:42 pm

    Hi, very nice post. I have been wonder’n bout this issue,so thanks for posting

  15. Eric on June 20, 2009 3:07 am

    Hi guys,
    Put this line in your source code.
    extern NSString *WebViewProgressEstimateChangedNotification;

  16. Jon Meadowbrook on July 6, 2009 12:42 pm

    will this work with the 3GS?

  17. SiDart on August 5, 2009 1:41 am

    ??????? ? ?????????, ???????? ?? ?????

  18. Koshan on August 6, 2009 2:13 am

    ????? ????? ???????? ? ?????????, ??? ???????????

  19. greg on November 13, 2009 12:45 pm

    I think this notification name has changed to WebProgressEstimateChangedNotification

  20. deen on January 11, 2010 11:41 pm

    Excellent posts! :D

    However, I compared the available methods in your header files and from the Mac OS webkit reference. It seems there are some methods missing: e.g. –

    (void)setSelectedDOMRange:(DOMRange *)range affinity:(NSSelectionAffinity)selectionAffinity

    I tried to manually add this method, but there will be errors upon compilation. Do you know how to fix this, or the iPhone webkit just doesn’t support this?

  21. eric m on January 20, 2010 10:41 pm

    omg…i have to know how you figured this out…Thanks, works great!

  22. eric m on January 27, 2010 8:25 pm

    i’m back… i loved this work around…unfortunately , as of writing, Apple doesn’t like it. They rejected my app for this use…but it’s still cool, and there must be a way to implement this “legaly”

Write a Comment