21

I'd like to add a header view to an UIWebView similar to the address/search bar in MobileSafari and the excellent Articles.app by Sophia Teutschler. More precisely, I'd like to create a "pull to fix orientation" view above a UIWebView, just like in Articles. Articles does use a UIWebView, so it seems to be possible. Is there a way to accomplish this without having to embed the UIWebView into a UIScrollView and updating its size all the time, as described in this article? Apparently, I do need the scrolling events to have the "pull to fix orientation" behave accordingly.

Community
  • 1
  • 1
MrMage
  • 7,282
  • 2
  • 41
  • 71

7 Answers7

11

After several attemps, I've decided to go with this solution:

Basically I've just added my header UIView as a webview.scrollview subview.

To avoid having the header overlap the browser view:

  • cycle through all [webview.scrollView subviews]
  • look for the biggest, wich should contains the browser (the others are for shadows and lines)
  • change its origin.y

    here's the code:

    CGRect browserCanvas = web.bounds;
    for(int i=0; i< [[web.scrollView subviews] count]; i++)
    {
      UIView* subview = [[web.scrollView subviews] objectAtIndex:i];
      CGRect f = subview.frame;
      NSLog(@"sub %d -> x:%.0f, y:%.0f, w:%.0f, h:%.0f", i, f.origin.x, f.origin.y, f.size.width, f.size.height);
    
      if(f.origin.x == browserCanvas.origin.x && 
         f.origin.y == browserCanvas.origin.y && 
         f.size.width == browserCanvas.size.width && 
         f.size.height == browserCanvas.size.height)
         {
             f.origin.y = header.frame.size.height;
             subview.frame = f;
         }
    }
    
    if(![header superview])
    {
      [web.scrollView addSubview:header];
      [web.scrollView bringSubviewToFront:header];
    }
    
Kenny Winker
  • 11,919
  • 7
  • 56
  • 78
sosergio
  • 954
  • 2
  • 13
  • 25
  • 3
    What about using the contentInsets of the scrollView (and set your frame y to be negative) ? – ıɾuǝʞ Feb 09 '12 at 20:43
  • 1
    @kenji that works well, as @Manesh details in another Answer. Except for the case when your webView has an element with the css position set to `fixed-top`. If you're expecting widely varied web content, you WILL encounter a page with a fixed-top navigation bar so @sosergio's suggestion works best. – Kenny Winker Oct 25 '13 at 21:32
7

I think the best solution is to add the header as a subview of your UIWebView’s scrollView, adjust UIEdgeInsets, and in the scrollViewDidScroll delegate function adjust the scrollIndicatorInsets as needed.

Every other solution I found didn't account for the scroll indicators.

Since getting the behavior just right has a bit of complexity to it, I decided to write a class to handle all of it: MMWebViewWithSmartHeader. It's still a little rough around the edges but should help.

Manesh
  • 176
  • 1
  • 5
  • +1, although I wasn't able to get things working with a `UIImageView` set as the headerView. I had to wrap the `UIImageView` inside a `UIView`. – FreeAsInBeer Apr 25 '13 at 18:25
  • Not bad, but ugly when the webview is zoomed, or panned on x axis. The header should lock on x axis and shouldn't be affected by scrollview zooming. – Martin Jun 20 '13 at 08:21
  • My bad: zooming behavior is good, but the x axis problem remains. – Martin Jun 20 '13 at 08:33
  • 1
    add `CGRect headerFrame = self.headerView.frame;` `headerFrame.origin.x = scrollView.contentOffset.x;` `self.headerView.frame = headerFrame;` at the end of your `- (void)scrollViewDidScroll:(UIScrollView *)scrollView` to solve the problem – Martin Jun 20 '13 at 08:39
  • i had a problem where after adding a subview to the webview's scrollview half the webpage would be unclickable, i fixed it by inserting the subview at position 0 (so its at the back in the view hierarchy), not sure why it works but it did. – Fonix Jul 30 '13 at 09:27
  • i also had a problem when viewing the webview in landscape mode, the header bar would not be clickable, i fixed it by means of [this answer](http://stackoverflow.com/a/17966145/1219956) – Fonix Jul 31 '13 at 09:16
  • This answer also has some issues when the webView is showing content with a navbar that's css position is `fixed-top`. I had to use @sosergio's answer. If you control the web content, this solution is more elegant, but if you're going to be viewing random web pages, you're going to encounter a fixed-top nav-bar. – Kenny Winker Oct 25 '13 at 21:35
4

I like AutoLayout specially because I can make the height of the header dynamic.

This is my code using AutoLayout in a controller with a reference 'headerView' to the header view and a 'webView' to the webView.

// Function called in ViewDidLoad:
func addHeaderToWebView(){
    // We load the headerView from a Nib
    headerView = NSBundle.mainBundle().loadNibNamed(WebViewHeader.nibName, owner: self, options: nil).first as! WebViewHeader

    headerView.translatesAutoresizingMaskIntoConstraints = false

    webView.scrollView.addSubview(headerView)

    // the constraints
    let topConstraint = NSLayoutConstraint(item: headerView, attribute: .Bottom, relatedBy: .Equal, toItem: webView.scrollView, attribute: .Top, multiplier: 1, constant: 0)
    let leftConstraint = NSLayoutConstraint(item: headerView, attribute: .Leading, relatedBy: .Equal, toItem: webView, attribute: .Leading, multiplier: 1, constant: 0)
    let rightConstraint = NSLayoutConstraint(item: headerView, attribute: .Trailing, relatedBy: .Equal, toItem: webView, attribute: .Trailing, multiplier: 1, constant: 0)

    // we add the constraints
    webView.scrollView.addConstraints([topConstraint])
    webView.addConstraints([leftConstraint, rightConstraint])
}

// Function called in viewWillLayoutSubviews: to update the scrollview of the webView content inset
func updateWebViewScrollViewContentInset(){
    webView.scrollView.contentInset = UIEdgeInsetsMake(headerView.frame.height, 0, 0, 0)
}
GaétanZ
  • 4,870
  • 1
  • 23
  • 30
0

Try this:

    UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(0, -100, WIN_WIDTH, 100)];
    redView.backgroundColor = [UIColor redColor];
    self.view.scrollView.contentInset = UIEdgeInsetsMake(100, 0, 0, 0);
    [self.view.scrollView addSubview:redView];
    NSURLRequest *request = [NSURLRequest requestWithURL: [NSURL URLWithString:@"http://www.baidu.com"]];
    [self.view loadRequest:request];
binku87
  • 149
  • 2
  • 4
0

Articles may not be using a UIWebView to display the main article text. To accomplish your task, UIScrollView will be required almost certainly. Resizing its subviews may not be necessary. Check out the UIScrollView code samples provided by Apple.

Nick Toumpelis
  • 2,717
  • 22
  • 38
0

I could not say for sure, but Twitter seems to use UITableView to achieve this. Perhaps you can try to put your view in the header of the table view, and your content in a cell.

Here is an interesting example you might consider looking at - How to make a Pull-To-Reload TableView just like Tweetie 2

koo
  • 2,888
  • 1
  • 23
  • 29
  • 1
    I don't have any problems with implementing this stuff for any sort of `UITableView`, but I need to have it for an `UIWebView`. – MrMage Nov 05 '10 at 23:20
  • 2
    I think Adam was suggesting to make a tableView with a single custom cell that contains a UIWebView with your web content loaded in it. – slycrel Nov 13 '10 at 07:29
0

Enormego published some sample code to make the pull-to-refresh thing happen:

https://github.com/enormego/EGOTableViewPullRefresh

It's really not so complicated. You can get a Safari/Articles-style header bar in your table view simply by creating your header bar and just adding it to your UITableView as a subview. I'm doing exactly that on the app I'm working on now:

GBSpendingMeter *meterView = [[[GBSpendingMeter alloc] initWithFrame:CGRectMake(0,0,320,44)] autorelease];

// The meterView will appear pinned to the top of the table, similar to a headerView
[self.tableView addSubview:meterView];

The tricky part is observing the scroll events on the table (FYI, UITableView is a subclass of UIScrollView so all the same notifications/callbacks should work), but the sample code I linked to has a good example of how that works.

David Demaree
  • 385
  • 1
  • 10
  • 3
    I don't have any problems with implementing this stuff for any sort of `UITableView`, but I need to have it for an `UIWebView`. – MrMage Nov 05 '10 at 23:19