Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIWebView inside a UIScrollView

My application has several UIWebView views inside a UIScrollView. Sometimes I have html content so I load the UIWebView with the UIWebView loadHTMLString:baseURL: method. This does not seem to cause a problem. Recently I started loading some of the UIWebView instances with a url using the loadRequest: method. Once I started doing that, any time I scroll, I get the following error:

* Terminating app due to uncaught exception 'CALayerInvalidGeometry', reason: 'CALayer position contains NaN: [nan nan]'

With the following stack trace:

Thread 1, Queue : (null)
#0  0x01dd5caa in objc_exception_throw ()
#1  0x01f5da48 in +[NSException raise:format:arguments:] ()
#2  0x01f5d9b9 in +[NSException raise:format:] ()
#3  0x007e8c0d in CA::Layer::set_position(CA::Vec2<double> const&, bool) ()
#4  0x007def55 in -[CALayer setPosition:] ()
#5  0x037b07d3 in -[WebFixedPositionContent scrollOrZoomChanged:] ()
#6  0x00a5bfae in -[UIWebDocumentView _updateFixedPositionContent] ()
#7  0x00c66ff3 in -[UIWebBrowserView _updateFixedPositionContent] ()
#8  0x00a5b07e in -[UIWebDocumentView _didScroll] ()
#9  0x01fb6dea in -[NSObject performSelector:] ()
#10 0x01f207f1 in -[NSArray makeObjectsPerformSelector:] ()
#11 0x0091627d in -[UIScrollView(Static) _notifyDidScroll] ()
#12 0x009029ae in -[UIScrollView setContentOffset:] ()
#13 0x00909ac8 in -[UIScrollView _updatePanGesture] ()
#14 0x0090d3c0 in -[UIScrollView handlePan:] ()
#15 0x00b84e29 in _UIGestureRecognizerSendActions ()
#16 0x00b84133 in -[UIGestureRecognizer _updateGestureWithEvent:] ()
#17 0x00b853bf in -[UIGestureRecognizer _delayedUpdateGesture] ()
#18 0x00b87a21 in ___UIGestureRecognizerUpdate_block_invoke_0541 ()
#19 0x00b8797c in _UIGestureRecognizerApplyBlocksToArray ()
#20 0x00b803d7 in _UIGestureRecognizerUpdate ()
#21 0x008e51a2 in -[UIWindow _sendGesturesForEvent:] ()
#22 0x008e5532 in -[UIWindow sendEvent:] ()

I have looked at the many questions and answers regarding this and realize that apple discourages this, but I haven't seen any answers that provide an alternative.

My first question: Is there an alternative way to achieve the same effect?

Below is a snippet of how I create the UIWebView

- (void) createWebView:(NSString*)url{
NSInteger requestedHeight = 480;
NSInteger requestedWidth = 640;

self.webContent = [[UIWebView alloc] initWithFrame: CGRectMake(0, self.frame.size.height, requestedWidth, requestedHeight)];
self.webContent.scrollView.userInteractionEnabled = NO;
self.webContent.scrollView.scrollEnabled = NO; 
self.webContent.scrollView.bounces = NO;

NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
[self.webContent loadRequest:urlRequest];

[self addSubview:self.webContent];

NSInteger calculatedHeight = self.webContent.frame.origin.y + self.webContent.frame.size.height;
NSInteger calculatedWidth = self.webContent.frame.origin.x + self.webContent.frame.size.width;
if (calculatedHeight > self.frame.size.height || calculatedWidth > self.frame.size.width){
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, MAX(self.frame.size.width, calculatedWidth), MAX(self.frame.size.height, calculatedHeight + 5));
}    
}

As you can see, I disable scrolling and user input for the UIWebView.

My second question: Why is the UIWebView (or at least, one or more of its subviews) handling scrolling at all when I have explicitly disabled that functionality?

My third question: Since it seems to be handling scrolling anyway, is there any other way to intercept or disable the scrolling notification that gets sent to the UIWebView?

As you can see, I do not really intend to let the user interact with the content of the UIWebView - just allow them to see the content.

In this scenario, would a possible alternative be to somehow create a UIWebView outside of the UIScrollView that is not visible to the user, capture its content into an image and then display the content in a UIImageView? If so, how would I capture the content of the UIWebView into an image?

EDIT: I found a solution for this that was quite easy. Basically load your UIWebView but don't add it as a subview. When the UIWebView finishes loading, create the image with the code below:

- (void)webViewDidFinishLoad:(UIWebView *)webView{
UIGraphicsBeginImageContext(self.webContent.bounds.size);
[self.webContent.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

[self.imageContent setImage:image];
}

I would still be interested in hearing answers to questions 1 to 3. In the future, if we decide to support interactive UIWebView's, this solution will not work.

Any suggestions?

like image 707
user1531567 Avatar asked Jul 17 '12 11:07

user1531567


2 Answers

Have you been testing this code on the iOS simulator or an actual device?

I've recently experienced the same crash when testing on iOS 5.1 in the simulator, but do not experience the same crash on iPad/iPhone running 5.1.

iOS 6 works fine on devices as well as the simulator.

I believe this is due to a UIWebView change in iOS 6, UIWebView has been changed to draw its content asynchronously now.

like image 190
Nick Avatar answered Oct 15 '22 06:10

Nick


check for the NaN values when trying to set the property with:

BOOL isNaN = isnan(your_Value);

so you can handle the values properly, once you have this and you can run the code, it would be good to find out why is this happening in the first place (I would guess some dodgy calculation when getting the value you trying to set)

Also, here is the NaN explained: http://en.wikipedia.org/wiki/NaN

like image 31
Ondrej Rafaj Avatar answered Oct 15 '22 05:10

Ondrej Rafaj