Mar 26

A UITextView is great for displaying multiple lines of simple text. But when you need something more fancy, like headlines or highlighting text, then you should look at UIWebView. It’s not just capable of displaying web pages from a URL, but you can also specify the HTML you want to display as a string.

Create a UIWebView in a controller:

- (void)loadView
{
  // Create a custom view hierarchy.
  CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
  UIView *view = [[UIView alloc] initWithFrame:appFrame];
  view.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
  self.view = view;
  [view release];

  CGRect webFrame = [[UIScreen mainScreen] applicationFrame];
  webView = [[UIWebView alloc] initWithFrame:webFrame];
  webView.backgroundColor = [UIColor whiteColor];
  [self.view addSubview:webView];
}

Then you can add your rich text to the view like this:

NSString *html = @"<html><head><title>The Meaning of Life</title></head><body><p>...really is <b>42</b>!</p></body></html>";
[webView loadHTMLString:html baseURL:nil];

The documentation is not very clear on what the baseURL is used for in the context of HTML that is loaded from a string.

written by Nick \\ tags: ,

12 Responses to “Display Rich Text Using a UIWebView”

  1. Barron Says:

    Some of the older documentation suggests that “the base URL allows relative URLs within a document.” Haven’t tried it yet, but I assume this is for links within the html code you display on the UIWebView…

    http://developer.apple.com/documentation/Cocoa/Reference/WebKit/Classes/WebFrame_Class/Reference/Reference.html#//apple_ref/occ/instm/WebFrame/loadData:MIMEType:textEncodingName:baseURL:

  2. Lance Says:

    Your code snippets are very nice, have you considered putting up a very basic full sample src after each. A lot of tutorial sites take this tact after they present and explain the parts… hmm guess yours isnt a step by step… still. Given the iphone sdk doesn’t include examples (with its install at least)

  3. Skip Haughay Says:

    I am trying to figure out the best way to implement some functionality that requires styled text runs within a block of text, presented as part of what stylistically appears as a grouped list view. Whether or not this is the actual implementation remains to be seen by what we end up being able to actually do with the framework. I have been having a heck of a time getting this to work this week, so I may have to try a different approach.

    Here’s what we are trying to implement:

    The screen will contain is a grouped list of four or more items. TWO of the items are blocks of text, ie. a section entitled Overview and a section entitled Description. Inside the rounded rectangle item of the particular section there is but one cell. That cell will contain static text about this item. What we would LIKE to show in this is richly formatted text. This is something that is currently not possible with UILabel items. The secondary requirement is that we want a simple way that a non-programmer (the content expert using this) can edit their own text content for these cells in a layout editor of some sort, and have this information presented in the application at runtime for their customers. To this end, we thought that perhaps a UIWebView would be the ideal solution. We might also be able to have a version in which the application pulls the content from a remote web source, which would allow the trainer to customize the content presentation remotely.

    And now the fun begins.

    So this part of the program is organized as follows:

    There is a protocol that defines an abstract interface for a factory class, whose purpose is to create the four various cell types that are required for this particular screen. There is an instance of a concrete factory created that conforms to this protocol and contains a few required fields. This is passed into the designated initializer of my subclass of UITableViewController. This tableview controller conforms to the UITableViewDelegate protocol, so that it can use its factory object to create the required cells, provide group counts, names, etc, and cell size information.

    I have for my overview cell defined a subclass of the UITableViewCell, which contains a subview that is a UIWebView. I’m creating this from a nib, btw.

    SO through various contortions, I get the UIWebView supplied with the file URL of the HTML content it needs to display. It’s just text…

    So what I then try to do, or rather what I WANT to do, is have the content loaded, with the width of the UIWebView constrained to the dimensions set in the frame in IB. I want the height of the UIWebView to grow to whatever height is required to fully render the block of styled text contained in the file. I setup the HTML with a viewport meta tag that I thought would constrain the width and disable scaling, but am unsure if it actually is working as I wanted it to.

    So anyway, the UIWebView delegate gets called when the content is done loading. What I wanted to do there is constrain the width of the UIWebView, and size the height to that required to show all of the content. I try doing this by invoking sizeToFit. Interesting effect, but not quite what I wanted… I can’t figure out how to constrain it, and the UIWebView, despite being contained in the content superview of the cell, is not being CLIPPED to the view of the container. But that’s another problem.

    What I then wanted to do is cause the containing cell to be redrawn and resized to fit the height of the content in the UIWebView. I tried telling the table view that it needed redisplay, or needed to be layed out again, but neither of these seem to have any effect. So what I get is an unconstrained UIWebView with the content I want, overrunning the view of the parent container view within the cell.

    How to make this work?

    First question, I guess, is how to constrain the UIWebView in one direction and then query its height after it has loaded and sized its content.
    Second question is, how do I get the tableview to redraw itself and ask for the cell heights again?

    I can easily re-architect this so that the UIWebViews are created prior to the table creation, and then when I make the cells in the tableView:cellForRowAtIndexPath, I could size the cell based upon what I know as the size of the UIWebView, and just add it as a subview of the cell’s content view… But then how do I convert the size of the UIWebView cell, which is in pixels, to points, which is what ableView:heightForRowAtIndexPath: returns?

    I could also just NOT do this with a tableview, but rather just lay out my own set of views. But then the problem still remains, how do we constrain UIWebView in one direction, size it in another direction and then query what that new height is so that the containing view can be sized appropriately?

    Furthermore, this view will not always contain just four items. We could have multiple video placards (which invoke video playback in media player) as well as still image views, diagrams, etc. The contents are all data-driven, edited via plist goodness, and a sort of “data bundle” which is a hierarchy of folders containing assets, such as HTML for rich text, images, video assets, etc. In this manner, a non-programmer will be able to customize the content, and not bug me every time they want to modify the product.

    So any ideas? Any idea of a better approach that will meet these requirements? I can, of course, change the design requirements, but I am a stubborn SOB.

  4. John B Says:

    The baseURL is the base URL that relative URLs in the HTML are based on. So, in your example, with your base URL set as “http://www.hitchhiker.com/message”, if, in your HTML you had something like

    img src=”test.jpg”

    then the image that would be loaded would be at http://www.hitchhiker.com/message/test.jpg

  5. Frank Says:

    Do you know if there Is any way for the UIWebView to reference images that are already downloaded on the iphone? I’ve experimented with a few settings for baseUrl to no avail.

  6. Nick Says:

    @Frank: If you by “images that are already downloaded” you mean images that you have downloaded in your code, then the answer is yes. Store the images that you download in the Documents or tmp directory. And then set baseUrl to point to that directory. See this post for more information: UIWebView – Loading External Images and CSS

  7. Sharif Says:

    My questions is what is the best way to create a Frequently Asked Question tab (section) of my App. I’m using a table view where the data is pulled from a .plist file. The table view has a three level drill down to a detail view where I want the actual answer from a FAQ. Now this is no problem using UITextView. However I need to enable variable fonts, colors, and alignments within a view that displays text and UIWebView handles this great. But how can I do this or what is the best way to handle this requirement? The FAQs also have to be searchable so there lies another hurdle (I can worry about that later). I just want to make sure that I set this up correctly the first time so when it comes time to add search capabilities I wouldn’t have wasted too much time for the FAQs are many. Any thoughts? The FAQs consist of 18 main groups and each group has several sections (4-8) and each section has several (4-8) questions/answers.

  8. Nick Says:

    @Sharif: I would use a SQLite database to store the three navigation levels as well as the HTML for the FAQs. Then use SQLite full-text search. You could also continue using a plist file and then store the FAQs in separate HTML files. You can then search by loading each HTML file into a string and search within it, or load all the HTML into memory at once and search, depending on if speed or memory use is a priority.

  9. Sharif Says:

    @NIck: I am really having issue trying to load my html files from a .plist that will open in a UIWebView. The code I’m using:

    @implementation FAQDetailViewController
    @synthesize webFile;

    – (void)viewDidLoad {
    [super viewDidLoad];

    NSString *path = [[NSBundle mainBundle] bundlePath];
    NSString *webPath = [path stringByAppendingPathComponent:webFile];
    UIWebView *tempView = [[UIWebView alloc] initWithContentsOfFile:webPath];
    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:webPath]]];
    [tempView release];
    }

    Then I load the html files up here in a case statement….

    case 2: {
    FAQDetailViewController *faqdvController = [[FAQDetailViewController alloc] initWithNibName:@”FAQDetailView” bundle:[NSBundle mainBundle]];
    faqdvController.webFile = [dictionary objectForKey:@”faqDesc”];
    faqdvController.hidesBottomBarWhenPushed = NO;
    [self.navigationController pushViewController:faqdvController animated:YES];
    [faqdvController release];
    }

    Although the code compiles, I still cant view my html file in my webview. Do you see the problem?

  10. Nick Says:

    @Sharif: I’m not sure how this code compiles since there is no initWithContentsOfFile method on UIWebView. Initialize the UIWebView just like any view with initWithFrame. Then again, it doesn’t look like that UIWebView object is used for anything… The loadRequest method is difficult to debug, so I would use the loadHTMLString method instead.

    You can find complete working code that loads local files into a UIWebView here.

  11. Sharif Says:

    Thanks Nick,

    That sample code helps out with my issue. Thank you, thank you, thank you.

  12. Right Scale for a UIWebView | The App Business Blog Says:

    […] you’re using a UIWebView to display rich text from a string of HTML (as I described yesterday) you can even include HTML links to external […]

Leave a Reply