RichTextEditor is a Swift library that provides a UITextView
subclass with HTML visual-editing capabilities.
- Use this library if you want to create an App that interacts with raw HTML content.
- iOS 11 and above
- Xcode 12.2 and above
To run the Example app
After installing RichTextEditor, import the module and use the RichTextEditor.TextView
view as shown below:
import RichTextEditor
// ...
let textView = RichTextEditor.TextView(
defaultFont: UIFont,
defaultParagraphStyle: ParagraphStyle = ParagraphStyle.default,
defaultMissingImage: UIImage) {
RichTextEditor provides a UI component to display and edit HTML content in a performant way. It does not implement its text layout functionality. Instead, it builds on top of Apple's UITextView provided by the UIKit framework.
The library is composed of two main systems:
- TextView a user interface component that allows the presentation and editing of the HTML
- HTML converters that transform raw HTML to NSAttributedString and vice-versa
You can use each of the components separately, but they are all composed under the TextView class to provide a single element to display and edit HTML.
TextView is the core class of the system. It's a UITextView
subclass that implements custom subclasses of NSTextStorage
, NSLayoutManager
, and NSAttributeString
attributes to allow HTML editing.
There are two main tasks that this class handles:
- Maintenance of the custom
NSAttributedString
attributes and attachments as the user edits the content to represent a valid HTML string. - Presentation of the custom attributes, like lists and quotes
The TextView and TextStorage classes are responsible for the maintenance of the NSAttributedString
.
They handle user manipulation of formatting (strong, emphasis, lists, blockquotes), embeds (images, videos, separators), and the user interaction with them (line breaks, composition).
The formatting is implemented by the toggle(formatter, atRange)
method in combination with AttributeFormatter objects. The formatter objects are split into the following categories:
- AttributeFormatter that handle characters attributes like bold, italic,
- ParagraphAttributedFormatter, that control paragraph attributes like lists, blockquotes, headings, etc.
For embeds, like images, videos, separators, tables, etc subclasses of the following are used NSTextAttachment object:
The replace(at:NSRange, with: NSTextAttachment)
handles the insertion of embeds with multiple helpers methods to handle each specific type of embed.
The attachment objects work by delegating their presentation using the following protocols:
- TextViewAttachmentDelegate: for media elements where the data can be be downloaded asynchronously from the web. For example
img
andvideo
elements. - TextViewAttachmentImageProvider: for HTML elements, that have a static presentation like
HR
, HTML comments,More
,NextPage
.
Another essential task is interaction with user input and handling new lines, deleting list items, and indents around:
- Lists
- Quote
- Paragraphs
The following "ensure..." methods handle all these scenarios:
- 'ensureRemovalOfParagraphAttributesWhenPressingEnterInAnEmptyParagraph'
- ensureInsertionOfEndOfLine
- evaluateRemovalOfSingleLineParagraphAttributesAfterSelectionChange
These methods check if you are adding or removing new paragraphs around lists or quotes and update the attributes accordingly.
The LayoutManager class handles the presentation for the custom attributes that the standard NSLayoutManager is unable to do:
- Backgrounds and vertical bars for Quotes:
drawBlockquotes
- Backgrounds for Pre:
drawHTMLPre
- List bullets:
drawLists
The layout manager uses the extra information provided by the ParagraphStyle class to be able to render the correct background and bullets for each element.
The HTMLConverter class handles all the conversation between HTML and NSAttributedString. The main entry points are:
- attributedString(from:html, defaultAttributes), converts from HTML to a NSAttributedString object
- html(from:NSAttributedString, pretify) converts from NSAttributedString to HTML
Two classes are responsible for the conversion:
- HTMLParser transforms from HTML text to an in-memory XML tree.
- AttributedStringSerializer converts from the in-memory tree to NSAttributedString.
The HTMLParser
class uses the libXML2 library to read the raw HTML. It then transforms the document returned to a more developer-friendly DOM node tree.
The DOM node tree implementation has classes representing elements, text, CSS attributes, CSS values, and comments.
AttributedStringSerializer
then converts this DOM tree to an NSAttributedString.
The specialized NSAttributedString
has custom attributes that add extra information about the HTML elements that the string contains:
- HTMLRepresentation stores the original HTML elements and attributes that were in the initial HTML
- ParagraphStyle is a subclass on NSParagraphMutableStyle, that allows representation of a hierarchy of elements, for example, nested lists, blockquote inside lists, and others.
Specialized Converter classes transform from HTML Elements to NSAttributedString text and attributes, for example:
- ImageElementConverter converts
img
elements to a custom attachment, - GenericElementConverter transform
strong
elements to a font attribute - LIElementConverter transform
li
elements from a list toParagrahStyle
In a mirror process from the HTML to NSAttributed conversion, there are the following classes:
- AttributedStringParser converts from the
NSAttributedString
to an in-memory tree - HTMLSerializer transforms from the in-memory tree to an HTML string.
The AttributedStringParser
transforms the NSAttributedString
to a DOM tree. On a first pass, it iterates paragraph by paragraph and uses StringAttributesConverters and AttachmentConverters to convert from NSStringAttributes
and NSAttachments
to the DOM.
Then it goes through the DOM tree and merges nodes to create a simplified version of the tree.
The HTMLSerializer
is used to convert the DOM Tree to an HTML string.