Ad

Our DNA is written in Swift
Jump

NSAttributedString+HTML Q&A

Over the past few months I have received questions about NSAttributedString+HTML and Rich Text Editing. Here are the Frequently Asked Questions.

I generally abbreviate NSAttributedString+HTML as NSAS+HTML. If your question or app is not in this list please let me know.

General / Cost / Licensing

What is NSAS+HTML?

Cocoa on the Mac has an initWithHTML method on NSAttributedString. NSAS+HTML consists of 3 parts:

  1. A reverse-engineered and enhanced implementation of the Mac initWithHTML for use on iOS
  2. UI classes to properly render the created NSAttributedString
  3. Objective-C wrapper classes for CoreText to simplify it’s use

The goal of NSAS+HTML is to provide a comprehensive way to eliminate UIWebViews from your apps and reap the benefits of being able to fully control the way the rich text is rendered and how your users interact with it.

Where does it live? How do you contribute to it?

NSAS+HTML is hosted on GitHub. If you find problems then please open an issue there. If you fix a problem or add a new feature then you can send a pull request so that the modifications from your fork can be merged into the master repository.

Does NSAS+HTML contain private APIs?

No. All code was newly created by the original author Oliver Drobnik as well as several contributors. NSAS+HTML is not a web browser, but instead renders views described with HTML.

Is NSAS+HTML legal to be used in App Store Apps?

Yes. A growing number of apps is using NSAS+HTML, the examples I know about are:

Please let me know if your app is not in this list so that I can add it.

How is NSAS+HTML licensed?

NSAttributedString+HTML is licensed under a BSD license. This means that you can use it for free in free and commercial products as long as you mention in the app that it contains “NSAttributedString+HTML copyright by Oliver Drobnik / Cocoanetics”.

Binary apps must display such a copyright notice somewhere accessible to the user, for example in the settings or about page. Source code apps must contain the license text file that is part of the GitHub project.

Is there a way to use NSAS+HTML without attributing it?

Yes. You can purchase a non-attribution license for €75 for it that eliminates the need for the attribution while still being able to use it in your commercial and free apps.

Does NSAS+HTML support Rich Text Editing?

No. By itself NSAS+HTML only renders. A commercial component of mine, called DTRichTextEditor, builds on it and adds text input.

What is the difference between DTAttributedTextView and DTAttributedTextContentView?

DTAttributedTextView is a scrollview sub class that has a DTAttributedTextContentView as sole sub view. DTAttributedTextContentView sizes itself to automatically fit the text.

Fonts

What is the default font?

Times New Roman, 12 px. If you don’t specify any font attributes in the HTML this will be the font that is used. Headers are sized relative to that.

How do you change the default font size on DTAttributedTextView?

You can pass a dictionary with a variety of parameters to initWithHTML. One of these parameters is NSTextSizeMultiplierDocumentOption the text scale that causes all fonts to be a multiple of what their default would be. This affects all headers and body text equally.

Why does the first time attributed text is shown take 1 sec?

This is a bug in iOS that causes an extensive font matching table to be created the first time a CTFont is required. You have two possible workarounds available:

  1. Pre-initialize CoreText as shown here on a background thread.
  2. Put all the fonts you require in CoreTextFontOverrides.plist

If you only using one font then number 2 is the recommended method since it bypasses CoreText font matching.

What is the difference between Font, Font Face, Font Family and Font Variant?

A font family is the overall grouping of multiple fonts. Each font represents one look, like plain or bold. Font and Font Face are the same. Font is often used incorrectly synonymous with Font Family.

For example the Helvetica font family consists of these fonts/faces:

  • Helvetica
  • Helvetica-Bold
  • Helvetica-Oblique
  • Helvetica-BoldOblique

When NSAS+HTML requires a font face from a given family with a given size it will first look into the overrides plist (if present) and then use CoreText font matching.

Font Variants are different representations contained within the same font file/face. For example on iPad there is one font that contains also a small caps variant. A bold font is NOT a variant, it is a font in its own right.

Implementation

How can DTAttributedTextView be used in table view cells?

DTAttributedTextView should not be used in table view cells because it is a scrollview. You can either use DTAttributedTextContentView and use their automatically sized heights for the heightForRowAtIndexPath or use the specially created DTAttributedTextCell class for that purpose. Please refer to DemoSnippetsViewController.m to see how it is used.

Please note that if you are using a different height for each row this dramatically reduces table view scrolling performance. Instead we recommend to use a fixed height.

After implementing DTAttributedTextView URLs show up but cannot be clicked, why?

NSAS+HTML is only in charge of rendering the text, you have to take care of the interactivity because everybody might have different needs.

NSAS+HTML provides delegate methods asking for custom subviews to be put over links. You can use the DTLinkButton provided with NSAS+HTML and have this perform any action you like.

What is DTTextAttachment?

DTTextAttachment encapsulates any kind of object that the layout process should reserve space for. This could be an image or a video. The space that is reserved for displaying this is taken from the displaySize property.

How are images rendered?

NSAS+HTML has two built in methods to render images. Either you enable the shouldDrawImages property on the content view which will draw local images together with the text. Or you implement the delegate method to provide your own UIView to show the contents of the DTTextAttachment that encapsulates the image.

For local images the contents property of the attachment contains the UIImage, for remote images the contentURL provides the URL for lazy loading. NSAS+HTML provides DTLazyImageView which demonstrates how to lazily load images from remote URLs, please refer to DemoTextViewController.m to see how this is integrated.

Note that you have to trigger a re-layout once you have loaded a remote image as soon as the actually displaySize of the DTTextAttachment is known. This step is not necessary if you specify a width/height in the HTML tag because then the displaySize of the attachment is known for layouting.

Does NSAS+HTML support Floating of Images?

No. Floating allows images to be positioned on the left or right side of the document and have text flow around them. Currently layouting is done in a single pass with one layout frame. To support floating of text attachments multiple passes would be required to determine the optimal sizing of multiple rectangles to be filled with text.

Does NSAS+HTML support multiple column layout?

Yes and No. The provided UI classes only work with a single layout frame.

To get multiple column layout you have to create your own UIView that takes multiple layout frames. The steps are:

  1. Create your DTCoreTextLayouter from the attributed string.
  2. Create a DTCoreTextLayoutFrame for the first column specifying the rectangle
  3. Create a second layout frame for the second column specifying another rectangle and the starting index that is one higher than the ending index in the first column
  4. Repeat this for each column

Once you have all these layout frames your UIView subclass only needs to call renderInContext for all columns.

Does NSAS+HTML support CSS styles sheets?

Not yet, but some contributors are working on this. At present only styles that are contained in the individual tags are recognized.

Does NSAS+HTML also allow for creating HTML out of attributed strings?

Yes. There is a htmlString method in the HTML category. This needs much work still.

Does NSAS+HTML support Margin or Padding?

No. Those are attributes that require the distribute the text into multiple blocks. There is no block level support in NSAS, essentially one layout frame is one big block.

You can specify edgeInsets on all sides for  DTAttributedTextContentView which considers this inset on sizing the layout frame.


Categories: Q&A

33 Comments »

  1. How to make the DTAttributedTextView horizontal-scrollable just like the Page Control?

  2. That is not currently supported. What kind of use case would you see this for?

  3. just like the behavior in Float app

  4. This is done by having multiple DTAttributedTextContentViews next to each other and creating one DTCoreTextLayoutFrame per Page.

  5. Hello,

    I want to display context with one Image at the top. This should have no edges, but the text below should have…
    I use the didChangeImageSize method to change to size of the image.

    And when I set textView.contentView.edgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
    Everything has no edges 🙁

    How can I handle this?

    Thanks for your help,
    Stefan

  6. you can set your custom imageView with any size and coordinates you like. I would leave the edgeInsets, but instead only make the frame of the top image wider.

    Or you could create your own scrollview that has an imageView at the top and a contentView below .

  7. Hello,

    thanks for your fast answer 🙂

    I tried to use this method:
    – (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView viewForAttachment:(DTTextAttachment *)attachment frame:(CGRect)frame

    and here I change the frame, but this is not working 🙁

    What I’m doing wrong?

    Thanks again,
    Stefan

  8. I’ve never had the case before that somebody would want to specify a different frame than the layouter had reserved as space for it. Because of this the frame of all custom subviews is constantly adjusted to match the layout coordinates.

  9. Thanks again.

    So, it’s not possible to go this way?

    Stefan

  10. Using a DTAttributedTextContentView when you layout an html string it automatically resizes the view to the height needed to display the entire content. Is there a way to cause the html to be truncated (with trailing …) to a set height?

  11. The example project you have is a bit difficult to decipher because it is doing so many things in a single class. There seem to be a ton of great tools here, but if for instance someone just wanted to implement DTAttributedTextContentView within a UITableview cell, I don’t know if I could tell them what the minimum import is.

    I would assume you can import just the class that implements that method by the same name and the required dependencies would import accordingly based on their hierarchy, but it see a massive number of dependencies and “test” classes being required (some things appear to be missing as well from the current package if I try to build with a simple alloc of a DTAttributedTextContentView). What is the minimum set of files required to implement this?

    Much appreciated.

  12. I agree, the implementation and usage is not documented and there are a ton of dependencies. What is the fastest way to just use DTAttributedTextContentView?

  13. There is an open task to create a library/framework. Until then just copy all classes and headers to your project and remove the ones that have demo or test in their names

  14. You say that the default font is Times New Roman, 12 px. The size is the same for regular displays and for retina displays, so is it really 12px or 12pt?

  15. It’s POINT of course.

  16. +1

    It was kind of a surprise that the view resizes itself 🙂

  17. Still, stating which files exactly to copy to your project for the views to be used would be useful. Mentioning ‘everything’ doesn’t really narrow it down or improve understanding.

  18. This project is simply amazing!

    We are currently developing an App for iOS5 that uses the new UIPageViewController. Currently we are rendering every page as a separate view containing a CoreText context. The text string is scanned and using “CTFramesetterSuggestFrameSizeWithConstraints” we are creating an array of ranges of text to be used to display in every page the right amount of the string.

    Now we want to use your DTAttributedTextContentView in every page, in order to be able to display HTML-like format without writing a new HTML parser. The problem is: is it possible to split the DTAttributedTextContentView in multiple frames (one for page, with a view in every page) each containing right amount of text? In other words, we need to create multiple DTAttributedTextContentView, every one with a range of generated attributedString. Every advice would be appreciated!

    Thank you and sorry for my bad english…

  19. I’m facing exactly the same situation. Have you found a solution? Thanks.

  20. You simply create multiple layout frames with DTCoreTextLayouter and then the the layoutFrame to be shown by each pages’s DTAttributedTextContentView.

  21. Where can I get help with using DTCoreText?

  22. This has to be the most poorly documented git project I have ever seen.

  23. About the answer to “Does NSAS+HTML support multiple column layout?”, how can I detect there is no more content to be draw and I don’t need another column frame?

  24. You create layout frames specifying a range from the given attributed string. Then you can get the visibleStringRange from the frame. If the last index in there is less then your string length you need an additional layout frame.

  25. Why would I get error: no visible @interface for ‘NSAttributedString’ declares the selector ‘initWithHTML:options:documentAttributes:’
    NSAttributedString *string = [[NSAttributedString alloc] initWithHTML:data options:nil documentAttributes:nil];

    if I imported DTCoreText.h and NSAttributedString+HTML.h and had DTCoreText installed as a pod. What could be wrong in my project configuration?

  26. Nevermind, please delete my last comment, I typed initWithHTML instead of initWithHTMLData, stupid of me

  27. Where can I find the supported HTML tags and CSS properties?

  28. 1.
    DTCoreText.xcodeproj build active architecture only no
    ld: warning: ignoring file /Users/binwu/Library/Developer/Xcode/DerivedData/snailcommune-fwimmrnbfaiptdclthlzaarfrwwh/Build/Products/Debug-iphonesimulator/libDTCoreText.a, file was built for archive which is not the architecture being linked (i386): /Users/binwu/Library/Developer/Xcode/DerivedData/snailcommune-fwimmrnbfaiptdclthlzaarfrwwh/Build/Products/Debug-iphonesimulator/libDTCoreText.a
    Undefined symbols for architecture i386:
    “_DTColorCreateWithHTMLName”, referenced from:
    -[DemoTextViewController attributedTextContentView:viewForAttachment:frame:] in DemoTextViewController.o
    “_DTDefaultFontFamily”, referenced from:
    -[DemoTextViewController _attributedStringForSnippetUsingiOS6Attributes:] in DemoTextViewController.o
    “_DTDefaultLinkColor”, referenced from:
    -[DemoTextViewController _attributedStringForSnippetUsingiOS6Attributes:] in DemoTextViewController.o
    “_DTDefaultLinkHighlightColor”, referenced from:
    -[DemoTextViewController _attributedStringForSnippetUsingiOS6Attributes:] in DemoTextViewController.o
    “_DTGUIDAttribute”, referenced from:
    -[DemoTextViewController attributedTextContentView:viewForAttributedString:frame:] in DemoTextViewController.o
    “_DTLinkAttribute”, referenced from:
    -[DemoTextViewController attributedTextContentView:viewForAttributedString:frame:] in DemoTextViewController.o
    “_DTMaxImageSize”, referenced from:
    -[DemoTextViewController _attributedStringForSnippetUsingiOS6Attributes:] in DemoTextViewController.o
    “_DTUseiOS6Attributes”, referenced from:
    -[DemoTextViewController _attributedStringForSnippetUsingiOS6Attributes:] in DemoTextViewController.o
    “_DTWillFlushBlockCallBack”, referenced from:
    -[DemoTextViewController _attributedStringForSnippetUsingiOS6Attributes:] in DemoTextViewController.o
    “_NSBaseURLDocumentOption”, referenced from:
    -[DemoTextViewController _attributedStringForSnippetUsingiOS6Attributes:] in DemoTextViewController.o
    “_NSTextSizeMultiplierDocumentOption”, referenced from:
    -[DemoTextViewController _attributedStringForSnippetUsingiOS6Attributes:] in DemoTextViewController.o
    “_OBJC_CLASS_$_DTAttributedTextCell”, referenced from:
    objc-class-ref in DemoSnippetsViewController.o
    “_OBJC_CLASS_$_DTAttributedTextView”, referenced from:
    objc-class-ref in DemoTextViewController.o
    “_OBJC_CLASS_$_DTCoreTextFontDescriptor”, referenced from:
    objc-class-ref in CoreTextDemoAppDelegate.o
    “_OBJC_CLASS_$_DTCoreTextLayoutFrame”, referenced from:
    objc-class-ref in DemoTextViewController.o
    “_OBJC_CLASS_$_DTIframeTextAttachment”, referenced from:
    objc-class-ref in DemoTextViewController.o
    “_OBJC_CLASS_$_DTImageTextAttachment”, referenced from:
    objc-class-ref in DemoTextViewController.o
    “_OBJC_CLASS_$_DTLazyImageView”, referenced from:
    objc-class-ref in DemoTextViewController.o
    “_OBJC_CLASS_$_DTLinkButton”, referenced from:
    objc-class-ref in DemoTextViewController.o
    “_OBJC_CLASS_$_DTObjectTextAttachment”, referenced from:
    objc-class-ref in CoreTextDemoAppDelegate.o
    objc-class-ref in DemoTextViewController.o
    “_OBJC_CLASS_$_DTTextAttachment”, referenced from:
    objc-class-ref in CoreTextDemoAppDelegate.o
    “_OBJC_CLASS_$_DTVideoTextAttachment”, referenced from:
    objc-class-ref in DemoTextViewController.o
    “_OBJC_CLASS_$_DTWebVideoView”, referenced from:
    objc-class-ref in DemoTextViewController.o
    ld: symbol(s) not found for architecture i386
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

    2.
    my project build active architecture only no
    ld: warning: directory not found for option ‘-F/Users/binwu/Documents/蜗牛社区/SourceCode/ios/snailcommuneV2.2/snailcommune/lib/BaiduMap/Release-iphoneos’
    ld: warning: directory not found for option ‘-F/Users/binwu/Documents/蜗牛社区/SourceCode/ios/snailcommuneV2.2/snailcommune/lib/BaiduMap/Release-iphonesimulator’
    ld: warning: directory not found for option ‘-F/Users/jerry/Downloads/iOS_voice_1/lib’
    ld: warning: directory not found for option ‘-F/Users/binwu/Documents/蜗牛社区/SourceCode/ios/snailcommuneV2.2/snailcommune/lib/UMSocial_Sdk_Extra_Frameworks/Renren’
    ld: warning: directory not found for option ‘-F/Users/binwu/Documents/蜗牛社区/SourceCode/ios/snailcommuneV2.2/snailcommune/Supporting’
    ld: warning: directory not found for option ‘-FFiles’
    ld: warning: ignoring file /Users/binwu/Library/Developer/Xcode/DerivedData/snailcommune-fwimmrnbfaiptdclthlzaarfrwwh/Build/Products/Debug-iphonesimulator/libDTCoreText.a, file was built for archive which is not the architecture being linked (i386): /Users/binwu/Library/Developer/Xcode/DerivedData/snailcommune-fwimmrnbfaiptdclthlzaarfrwwh/Build/Products/Debug-iphonesimulator/libDTCoreText.a
    Undefined symbols for architecture i386:
    “_OBJC_CLASS_$_DTAttributedTextContentView”, referenced from:
    objc-class-ref in SCContentTableViewCell.o
    ld: symbol(s) not found for architecture i386
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

  29. Added

    self.attributedTextConView = [[DTAttributedTextContentView alloc] initWithFrame:CGRectMake(10.0f, CGRectGetMaxY(self.headerUserView.frame), self.contentView.width, 21.0f)];
    self.attributedTextConView.delegate = self;
    [self.customBackgroundView addSubview:self.attributedTextConView];

    Create object

    Error:
    ld: warning: directory not found for option ‘-F/Users/binwu/Documents/蜗牛社区/SourceCode/ios/snailcommuneV2.2/snailcommune/lib/BaiduMap/Release-iphoneos’
    ld: warning: directory not found for option ‘-F/Users/binwu/Documents/蜗牛社区/SourceCode/ios/snailcommuneV2.2/snailcommune/lib/BaiduMap/Release-iphonesimulator’
    ld: warning: directory not found for option ‘-F/Users/jerry/Downloads/iOS_voice_1/lib’
    ld: warning: directory not found for option ‘-F/Users/binwu/Documents/蜗牛社区/SourceCode/ios/snailcommuneV2.2/snailcommune/lib/UMSocial_Sdk_Extra_Frameworks/Renren’
    ld: warning: directory not found for option ‘-F/Users/binwu/Documents/蜗牛社区/SourceCode/ios/snailcommuneV2.2/snailcommune/Supporting’
    ld: warning: directory not found for option ‘-FFiles’
    ld: warning: ignoring file /Users/binwu/Library/Developer/Xcode/DerivedData/snailcommune-fwimmrnbfaiptdclthlzaarfrwwh/Build/Products/Debug-iphonesimulator/libDTCoreText.a, file was built for archive which is not the architecture being linked (i386): /Users/binwu/Library/Developer/Xcode/DerivedData/snailcommune-fwimmrnbfaiptdclthlzaarfrwwh/Build/Products/Debug-iphonesimulator/libDTCoreText.a
    Undefined symbols for architecture i386:
    “_OBJC_CLASS_$_DTAttributedTextContentView”, referenced from:
    objc-class-ref in SCContentTableViewCell.o
    ld: symbol(s) not found for architecture i386
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

  30. Hello, my body below a class need to link to their css file inside, but now the output attribute string does not have this picture, how to deal with more appropriate? Because the company project so hurry, looking forward to your reply very grateful.

    第8章

     

     

    诡异来电

     

     

  31. //第8章

    // 

    // 

    //诡异来电