Implementation of iOS Rich Text component-dtcoretext source parsing rendering

Source: Internet
Author: User

this article reprinted to http://blog.cnbang.net/tech/2729/

The previous article introduced dtcoretext how to convert html+css analysis into nsattributestring, this article then see how to render nsattributestring.

Coretext

The first simple introduction of the next Coretext,coretext is the ios/osx in the text rendering engine, all the text seen on the ios/osx in the bottom is to be rendered by the Coretext.

Coretext's going to put a line together. The same attributes of the text together as a ctrun, each line is a ctline, multiple lines together to form a ctframe. For example, the first line of text has two styles, the first part is bold, the second part is italic, because the style is different, so divided into two ctrun,ctline contains the two ctrun,ctframe contains all ctline.

A nsattributestring can be generated by the method provided by Coretext Ctframesetter,ctframesetter is used to create Ctframe factory, give Ctframesetter a Cgpath, Or simply understand that to give him a box, it will be generated by the Cttypesetter it holds ctframe,ctframe generated ctline and Ctrun are all generated, can be directly drawn to the canvas. The Ctframe/ctline/ctrun provides a rendering interface, but the first two are encapsulated, and finally the rendering interface to the Ctrun is actually called to draw.

If you want to render nsattributestring with Coretext, you can simply generate Ctframesetter, generate Ctframe, and draw UIView directly onto the current canvas in DrawRect Ctframe method:

12345678 - (void) drawRect:(CGRect)rect{     UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 320, 400)];     CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)content);     CTFrame frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0, 0), [path CGPath] , NULL);     CGContextRef ctx = UIGraphicsGetCurrentContext();     CTFrameDraw(frame, ctx);}

Coretext will render the text according to the style attributes in the nsattributestring. This is the simplest way to render coarse-grained, but if you need to do further processing of text rendering, such as adding a background color, such as Coretext not supported by the properties, or to insert a picture in the middle of the text, you can not simply draw ctframe, you need to line-by-row or Ctrun-by-one processing.

Overview

Dtcoretext need to deal with the text in the various types of attachment, and support text background color, paragraph indentation and other Coretext not supported properties, can not simply throw nsattributestring to coretext rendering, need to do more meticulous treatment. Dtcoretext divided into several layers, the overall structure of the chart:

The top level is the user, which can be a controller, such as the demotextviewcontroller of an example in a project, or a view class. Next is the Dtcoretext package of the various controls, with the Label,textview and cell, the text rendering of these controls are Dtattributedtextcontentview responsible, non-textual part of the sample/ The video and other elements will pass through the delegate to Dtattributedtextcontentview at the top user. Dtcoretextlayouter/dtcoretextlayoutframe/dtcoretextlayoutline/ Dtcoretextglyphrun these four classes correspond to the Ctframesetter/ctframe/ctline/ctrun in Coretext, imitate the coretext pattern, function and function, just add the function on the basis of them. Let's take a look at what each of these classes specifically does.

Dtattributedtextcontentview

Dtattributedtextcontentview inherits from UIView, as the middle layer of dtcoretextlayoutframe and upper control, is responsible for drawing the content according to demand, roughly doing the following several things:

1. Support Catiledlayer Segmented rendering

The UIView Layerclass is set to Catiledlayer to achieve sub-region rendering, that is, only render the area displayed on the screen, similar to those of the map app, mainly used for a control like TextView, which may be very long, to avoid rendering all the content at once, Render only the parts you can see to improve performance. After using Catiledlayer, in the-drawlayer:incontext: method with Cgcontextgetclipboundingbox through the context to obtain the currently displayed area, Dtcoretextlayoutframe only renders the contents of this area.

2. Generate Dtcoretextlayoutframe and draw

Dtcoretextlayouter and Dtcoretextlayoutframe are generated through the nsattributestring in the upper layer, and the text is rendered to the current layer with dtcoretextlayoutframe after various configurations. , including whether to show the picture link/limit lines/line break rules, and so on.

3. Handling Attachment and link

In the-layoutsubviewsinrect: method, traverse each of the dtcoretextglyphrun in the Dtcoretextlayoutframe, find the attachment and link to the run for processing, accessories including pictures/videos, etc. Create the corresponding view of these attachments, add these view to the Dtcoretextglyphrun calculated location to the customviews dedicated to the attachment and link.

In fact, the creation of these attachment view is in the upper user, Dtattributedtextcontentview through delegate the contents of each attachment and the corresponding frame to the upper layer to generate the corresponding view to return, This is estimated because the attachment to the processing of each user's needs are different, should not be directly written dead on the bottom, for example, some users require the image to enlarge after the click, the video needs to use their own controls and so on.

Dtcoretextlayouter

Dtcoretextlayouter is responsible for generating and caching dtcoretextlayoutframe, equivalent to Ctframesetter and ctframe relationships, and doing things very simple, is to generate the Ctframesetter from nsattributestring, and then generate dtcoretextlayoutframe based on different rect, and cache the frame.

Dtcoretextlayoutframe

Dtcoretextlayoutframe is the most important class that is responsible for rendering text, mainly doing two things: generating rows and rendering each row.

Generate Dtcoretextlayoutline

-_buildlineswithtypesetter: Creates a dtcoretextlayoutline of each row that is visible within the current frame scope, and the processing that is done during the creation process includes:

1. Support the whole paragraph indent

Remove the current line from the nsattributestring to see if there is a dttextblock that represents indentation, and if indentation is required, the width and position of the current line after indentation are calculated.

2. Support Truncation plus ellipsis

The upper layer, such as Label/textview, is restricted to a wide height, and if the content exceeds the width, the last line needs to be processed, and the "..." is added in the appropriate position.

One problem here is that you have to know what the last line to process is when you render to a line that is beyond the width of the height. For example, a textview height of 40, the text line height of 15, when the third row is rendered high to 45, the discovery has exceeded the height of textview, at this time know only to render to the second row, but the current has been processed to the third row, you need to take the second line out to truncate add "...".

In addition to exceeding the height, in excess of the external numberoflines also to be truncated, in order to unify the process, the practice here is to render out of height when the total number of rows of the record can be rendered (_numberlinesfitinframe), and then all again, Regenerate each line from start to finish, knowing how many rows are in total, and then processing the last row. The advantage of this is that it is simple and brutal to avoid repeating the code, the disadvantage is wasting performance, all the preceding lines are to be re-queued.

3. Support Hyphen

Hyphen is a ligature sign, which is to let the English words in the appropriate place to wrap and add a dash "-". Coretext native does not support hyphen, the only way to break the word break and the letter. Here hyphen is implemented by adding a placeholder 0X00AD, such as Location->lo-ca-tion->lo0x00adca0x00adtion, to all English words where the dashes can be added. 0X00AD is a non-visible character, Coretext does not render the character, but the position of the character can be line-breaking, Coretext no longer think location is a word, will wrap at the placeholder. Dtcoretext do the processing is if you find that the line is a placeholder 0X00AD, replaced by a dash "-", so in order to support hyphen, the content must be all words are written placeholder, otherwise invalid.

4. Calculate the position of each row in the current frame

When each row is generated, it is not known where the line is in the current frame, and it needs to be calculated manually. The x-coordinate of each row is easy to determine, but the calculation of the y-coordinate will take some effort. Factors to consider are the current row height, the previous line position, line spacing, segment spacing, padding,baseline, and so on.

, each line is based on baseline, need to calculate the baseline of this line in the current frame y-coordinate value, asent and descent is the value given by Coretext, Asent+descent is the row height. The process for estimating the baseline position of the current line is:

    • A. Calculates the last position of the previous line, i.e. Baseline+descent
    • B. Calculate half of the line spacing of the previous row, for example 1.5 times times the line spacing, that is ((1.5–1) *asent+descent)/2
    • C. Calculate half of the current line spacing, the algorithm is the same as above, except that the line spacing is not necessarily the same as the previous row. Here the two lines are counted in half for the neutralization of the distance between the peers.
    • D. The above calculation result is added together with the current row asent value, the baseline Y coordinate value of the current row is obtained.

In addition to the above main process, but also for the first line, the first paragraph of the paragraph, Dttextblock White and accessories attachment do the processing, the logic of the calculation in-_algorithmwebkit_baselineorigintopositionline.

5. Handling Alignment

To handle each alignment, right-aligned and Center-aligned, you need to calculate the x-coordinate value of the travel, justify the need to re-create a justified line through the Ctlinecreatejustifiedline method, for both ends of the two things here, one is not at the end of the paragraph to justify, Second, if the content length is not enough (the default is less than 60% of the width of the line) also do not justify the two ends, to avoid the text spacing stretching too bad effect.

6. Encapsulated into Dtcoretextlayoutline

After the above processing, each line of the Ctline object and the location of the line of information has been, the package into Dtcoretextlayoutline save, the task is completed.

Rendering

Dtcoretextlayoutframe provides a-drawincontext:options: method for rendering each of the above generated lines into the context canvas that is passed in. The process of doing this includes:

1. Draw the Dttextblock style

Dtcoretext support paragraph and background color, here will first find all the Dttextblock, through a series of troublesome methods to get the coordinates and size of these blocks, the corresponding background color drawn out.

2. Drawing Attachments

Implementation of the Dttextattachmentdrawing interface can be drawn together with the text here, in the Dtcoretext picture attachment is the implementation of the Dttextattachmentdrawing interface, you can directly draw the picture here. The actual film attachment rendering Dtcoretext provides two ways, the above describes the Dtattributedtextcontentview when the picture attachment can also be added to the upper layer of the user, to add themselves in the upper layer, You can pass parameters to tell Dtcoretextlayoutframe not to handle picture attachments when drawing.

3. Drawing text and Shadows

The final step is to iterate through each line of dtcoretextlayoutline and every dtcoretextglyphrun in the row, calling its-drawincontext: The method is drawn to the canvas one by one. When drawing, you need to calculate the location of each run, and call Cgcontextsettextposition to navigate to the specified position to draw the text. Drawing the text also handles the shadow effect, Coretext does not directly support the text shadow effect, but can use the Coregraphic interface to draw with the shadow, here also supports the existence of multiple shadow-_-!

Dtcoretextlayoutline

Dtcoretextlayoutline encapsulates the ctline and does things including:

1. Generate GlyphRun

By Ctline, you can take out the ctrun in this line, calculate the location of each ctrun, and encapsulate the dtcoretextglyphrun generated.

2. Calculating attributes and providing helper methods

The Asent/descent/lineheight and other properties are calculated and saved, and various auxiliary methods are provided to facilitate obtaining the information in this line, including the coordinates of the corresponding text by StringIndex, etc. Several methods related to ctline such as Ctlinegetoffsetforstringindex ()/Ctlinegetstringindexforposition () also have corresponding encapsulation.

Dtcoretextglyphrun

Dtcoretextglyphrun do things like Dtcoretextlayoutline, just in the rendering method to do something extra, first support text background color, which is coretext native unsupported, if attribute has background color properties, It will be drawn here. And then support iOS6 the following text underline and strikethrough, iOS6 before Coretext is not support underline and strikethrough, here to do its own processing to draw it up.

Summarize

The core of the entire process is the implementation of dtcoretextlayoutframe generation lines and rendering, equivalent to the Coretext native Ctframesettercreateframe/ctframedraw again to achieve their own again, In the implementation of the process coupled with their own special needs, from which we can also get a general understanding of ctframe/ctline internal implementation is how. Coretext has provided a sufficiently fine-grained interface to allow users to freely typeset as they wish, and Dtcoretext this series of processing gives a good example for reference.

Implementation of iOS Rich Text component-dtcoretext source parsing rendering

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.