Uiwebview in uiscrollview tips - objective-c

I have a UIScrollview to allow for pagination, and a UIWebview on the detail views inside. I know you are not supposed to do that according to Apples Class Reference, but I don't see another option.
In order to make responses and experience a bit better, I disabled scrolling of the UIWebview, with:
[[[webView subviews] lastObject] setScrollEnabled:FALSE];
But now I am looking for a solution to make the webview the right size, depending on the content. Is there an easy way to do this, where the width of the webview stays the same, but the height grows (automatically) when that is required to show the full article? This will remove the scrolling problem, as it sit he scrollview who will take care of all the scrolling in this scenario.
Does that make sense?

From the documentation for UIWebView, it states:
Important: You should not embed UIWebView or UITableView objects in
UIScrollView objects. If you do so, unexpected behavior can result
because touch events for the two objects can be mixed up and wrongly
handled.
A more appropriate approach would be to add your UIWebView to a UIViewController with a UIScrollView (or UITableView) underneath. I'd be conscious of the amount of space you'll have left though for the scroll view though, especially if you're dealing with the iPhone.
Hope that's a nudge in the right direction.
Update:
After looking into this (I was sure there would be a way), it seems the best approach I managed to come up with is to add your UIWebView to the header view of a UITableView.
//create webview
_webView = [[UIWebView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 280.0)];
//load request and scale content to fit
[_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.stackoverflow.com/"]]];
[_webView setScalesPageToFit:YES];
//add web view to table header view
[[self tableView] setTableHeaderView:_webview];
This adds a UIWebView to the header of the table view before the cells are displayed underneath. Note that a table header view (and footer views also) are different from section headers (and footers) so you can still use these two if you are using UITableViewStylePlain.
Be sure to attenuate the frame of the webview to allow for any cells underneath to be visible or else the user will not be able to scroll the tableview as any touches will be intercepted by the webview.
This still goes against the UIWebView documentation (stated above) but, in practice, this implementation actually works fine as long as the webview does not cover the entire scrollable region of the view.
Here's a screenshot of the test app I made:
Hope that helps :)

There is a better way to do this.
From iOS 5.0 onwards, apple expose the UIScrollView present inside the UIWebView. So to resize the webview based on the contents, here is what you do.
Make the parent of UIWebView the delegate to that UIWebView.
Implement the webViewDidFinishLoad: and in that function just do this.
CGSize sz = [webView.scrollView contentSize];
CGRect rect = webView.frame;
rect.size.height = sz.height;
webView.frame = rect;
Even if there are multiple load start and finish, it does not matter since eventually we will have the web view resized to fit the contents.
In case you don't want the user to see the dynamic resizing, you can just set the alpha of webview to 0 and when everything loads and you are done with resizing, you can transition it to alpha 1 with animation.

Related

Change layer of images

Is there a way to change the "layer" that UIImageView objects are drawn in? Whenever I add an image view to a view controller it defaults to drawing the most recently added one on top of all the others. So if I decide to add a "background" image it is now a "foreground" image and blocks everything else.
There isn't anything in the IB options or in the UIImageView class reference and I haven't been able to find anything here on SO. It's been a problem for a while and it's weird that I haven't seen anything about it before... I think it might just be my semantics coming from a delphi background.
Anyway, does anyone know about this issue / ICANHAZTEHCODEZ to fix it? Or is this just like the UIScrollView problem and poorly supported by the development environment.
This happens when I try to use the editor to arrange the subviews.
You can bring a SubView to Front or Send it background programmatically using
[self.view bringSubviewToFront:yourImageView];
and
[self.view sendSubviewToBack:yourImageView];
when self.view must be the superview of your imageView
In IB, select a UIControl and from top menu bar select Editor->arrage->send to front or back
When you use UIView's addSubview: method, it will add it to the top of the view stack resulting in what you are seeing.
There are numerous other UIView methods you can use to determine the order of subviews. E.g.:
- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview;
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index;
- (void)sendSubviewToBack:(UIView *)view;
- (void)bringSubviewToFront:(UIView *)view;

Using a patternimage for a View inside an NSScrollView

I have a quite big problem, I am really not able to solve myself.
The result should look like this:
This image was made with photoshop and is part of the interface I try to build.
In the middle you see something, that should be a list of projects, you should be able to scroll, if it the list is bigger then the view.
So I am making a scrollview like this: (for some reason I cannot do this in the interface builder and want this to work programmatically)
NSScrollView *projectsListView = [[NSScrollView alloc] initWithFrame:NSMakeRect(15, 2, 801, 588)];
[projectsListView setHasVerticalScroller:YES];
Then I create the content view and set a pattern image as backgroundcolor:
NSClipView *contentView = [[NSClipView alloc] initWithFrame:NSMakeRect(0, 0,
[projectsListView frame].size.width, [projectsListView frame].size.height+(98*2))];
[contentView setBackgroundColor:[NSColor colorWithPatternImage:[NSImage imageNamed:#"BoxLineBackground"]]];
[contentView setDrawsBackground:YES];
Then set the view as document view:
[projectsListView setDocumentView:contentView];
Should work, right?
However the content view gets clipped and looks like this while scrolling:
I tried this to fix it, but it does nothing:
[[projectsListView documentView] setCopiesOnScroll:NO];
I also tried this, but it causes the contentview not to scroll at all.
The image stays the same, but I can move the scroller normally.
[[projectsListView contentView] setCopiesOnScroll:NO];
If I try to set the contentview with setContentView: instead of using setDocumentView:
it may work, but the scroller is gone, so it is also not working correctly.
I would really like to use the patternimage method, because I cannot tell how long the list will be. It depends on the user.
An additional problem then would be to get the whole thing rounded, but that does not matter that much. I tried to use a transparent border image and to overlay the NSScrollView with it using an NSImageView, but again this causes corruption, because it clips and moves the overlaying parts of the image view together with the content of the scrollview.
Anyone having an idea, how to achieve this?
Thanks
Rather than re-inventing the wheel, this interface should be implemented with a view-based NSTableView. The table cell UI could then be created in Interface Builder and you could control the background of the cells using the various NSTableView delegate methods. NSTableView will handle redraws upon scrolling correctly.
To handle the pattern color, just make the background of your cell a custom subclass of NSTableCellView and implement your pattern drawing code.
Regardless of all this, the problem you are having is due to an NSScrollView drawing optimisation. You can turn this off by calling [[yourScrollView contentView] setCopiesOnScroll:NO] on your NSScrollView instance. You can also set this in Interface Builder, just un-check the Copies on Scroll checkbox.
I fixed the problem by setting the Background Color on the NSScrollView instead on the NSClipView.
I though the background would be static in that case and I need to set it for the content view for that reason, but it works pretty well and does scroll together with the content view.
And thanks for Rob Keniger's answer. I will probably try this out.

Cause UIScrollView zoomToRect: to only display a portion

I have a UIScrollView subclass that contains a UIImageView. When the user double taps on the zone I want to zoom to that CGRect (calculated from CGPoint) and "hide" the rest of the UIImageView. The final effect is similar in Mavel.app and The Walking Dead.app when you are reading a comic.
Until now I got:
-(void)presentRect:(CGRect)rect {
self.bounds = originalFrame; //1
[UIView beginAnimations:#"navigateToPos" context:nil];
[self zoomToRect:rect animated:NO];
self.bounds = rect;
[UIView commitAnimations];
}
This works, but "zoomToRect" needs the whole bounds of the UIScrollView and when I restart it (line 1), it gives an undesired effect.
I am stuck with this. I don't know if I need a new approach or need to use another property of UIScrollView.
Any help is appreciated. Thanks.
I downloaded this apps to see it. I'm almost sure they didn't even use uiscrollview. The way the pages moves make me think about that. And uiscrollview should be used only when you really need it, because it have a lot issues when you're customizing. (tiled content, zooming and others problems).
Slide to a next page only 50px aprox., the page goes to the next, in uiscrollview it didn't happen because it works diff.
As I said before, probably it's made using 4 bars (as you correct me) or it can be done with a view wrapping the uiimage and working as a mask. You only need calculate the size of the zoom square and move it to center, and resize the wrapper view to fit this new size. The substitute for the uiscrollview for pages can be a simple image gallery, but where is images you put this "comic-view".
I've solved using other properies from UIScrollView simulating the desired effect.

iPhone view controller view shifted down 20 pixels when rotating to landscape

I am trying to implement a custom tabbarcontroller using a UIViewController and a UITabBar. Everything is working fine so far, except when I rotate the device to landscape, the UIViewController shifts the entire view down 20pixels, creating a black bar between the status bar and the view. Even when the view is rotated back to portrait orientation, the view is still shifted down 20pixels.
What do I need to do to remove the black bar? Do I have to perform custom rotations? If so, where should I insert these rotations?
Before rotation:
After rotation:
I've experienced this same issue.
This discussion was helpful: UIWebView on iPad size
Tony's answer helped me discover that when you create a subview it's helpful to setup code similar to this:
UIWebView *webView = [[UIWebView alloc] init];
webView.frame = self.view.bounds;
If you post some of your code I may be able to provide more specific advice.
Rather than shifting all views down, try DELTA properties in Interface builder.

How to synchronize scrolling of a NSScrollView and a WebView?

My app has two main views: a NSTextView subclass within a NSScrollView and a WebView. What the WebView displays is dependent on what the user enters into the text view - so I would like when the user scrolls either the text view or the web view the other scrolls proportionately to it.
I found this article which mentions how to do it with 2 scroll views. My problem is that WebKit doesn't seem to use normal Scroll views anywhere.
How should I implement this? What am I missing?
This is not a trivial problem to solve perfectly, as it's difficult to know whether the amount of text being edited in your text view corresponds to a similar amount of scrolling in the web view.
However, to answer your question about scroll views in WebView, they are used but as far as I know not documented extensively. You have to take advantage of the fact that you can obtain the scrollview being used, using public API, by asking the appropriate WebView subview for its "enclosingScrollView". Something like this works for me in a WebView where I know that there is only one frame:
[[[[myWebView mainFrame] frameView] documentView] enclosingScrollView];
If they are scrolling proportionately, probably the simplest solution would be to override touches events on your UIScrollview and impliment stringByEvaluatingJavaScriptFromString: on the UIWebView with (js) window.scroll(x,y). However, scrolling from the webView to the textview will require more work. The webView eats touches events, so you would need to
create a top level UIView or UIScroll view which captures the touches and sends them on to both the scrollView and the webView (via javascript) for ALL touches events, or
use a gesture recognizer to do the same.