This article continues the Core Text thinking in practice with three examples
Controlling the position of each word has already been implemented.
Core Text practice: Customize the position of each word
One grid, one word
One word, one rich text, one CTLine,
First draw the background, then draw the text, can be
Example 1, based on the above, implement automatic line wrapping
The original two lines of “toon \ N red and I chun”,
Convert to a line of “toon red and toon”,
The rest is easy
Turn each word into a grid,
If you encounter a space, skip it
Model to deal with
The model looks like this,
struct Coupling: Decodable {
var string: String
let type: Int
}
Copy the code
The data is a list, [Coupling]
Now we’re going to merge,
I can put it on a line, I can put it on a line,
If you can’t put it on a line, wrap it
Data Styles: Title, many grids, Title, other (omitted)
// This is result var info = [Coupling]() var current: Coupling? for jujube in list{ switch jujube.type{ case 4: If var c = current{// See the let reduceTmp c.s. tring + = "" + jujube. The string / / more than one line if reduceTmp fourW > = TextContentConst. WidthInUse {/ / add the current Info.append (c) // Update current text current = jujube} else{// No more than one line, Use the added c. tring = reduceTmp current = c}} else{// enter a group of cells, take the first current = jujube} default: If let c = current{info.append(c) current = nil} info.append(jujube)}} If let c = current{info.append(c)} renderGrid = infoCopy the code
This way, the text is converted
The drawing part
func drawGrips(m info: TxtRenderInfo, lnH lnHeight: CGFloat, index i: Int, dB startIdx: Int, lineOrigin lnOrigin: CGPoint, context ctx: CGContext, lnAscent lineAscent: STRS [i-startidx] let glyphCount = content.count var frameImg = TextContentConst. FBgTypoImg let lnOffsset = (TextContentConst padding - lnHeight) * 0.5 var lineOrigin = lnOrigin LineOrigin. Y -= lnOffsset var textP = lineOrigin <glyphCount{// one word, A CTLine let pieX = String (content) [independence idx] let ln = CTLineCreateWithAttributedString (pieX. Word) let lnSize. = ln lnSize Float(idx + 1) // Text origin x value = image origin X value + offset textp. x = typeOriginX + (TextContentConsent.padding-lnsize.width) * 0.5ctx.textPosition = textP // Frame frameimg.origin. X = typeOriginX for the background image frameImg.origin.y = lineOrigin.y + lineAscent - TextContentConst.fBgTypoImg.size.height + TextContentConst.offsetP.y if pieX ! {// Not space // Draw background image bgGrip? .draw(in: frameImg) // Draw text CTLineDraw(ln, CTX)}} return lnOffsset}Copy the code
github repo
Example 2, word level control A
Each line has the proper spacing
From the whole rich text, to CTFrame,
From CTFrame, detach CTLine,
Part of CTLine, unwrap CTRun (that’s the left cell, the right line of text)
You just get the line number
The obtained data are as follows: the effect is relatively rich
Pinyin distance grid, 8 pt
Grid + text
One line of text, all grid
The rules above apply to those CTlines,
I need to create a mapping map,
Write down the line number of CTLine and the corresponding format
// Female/female/female/female/female Var wArr = [Int]() Var STRS = [String]() let CNT = list.count var I = 0 var index = 0 Var startIdx: Int? = nil while i < cnt { let ri = list[i] switch ri.type { case 1: // index += 2 forensics. Append (index) case 3: WArr. Append (index) case 4: // if startIdx == nil{startIdx = index} pairs. Append (index) strs.append(ri. String) default: // if startIdx == nil{startIdx = index} pairs. // index += 1} I += 1}Copy the code
Index, + 1 / + 2, and then add
Because there’s a title in front, title and body are separate,
The drawing part
Ctx.textposition = lineOrigin if info.contains(pair: I){// drawPairs(context: ctx, ln: line, startPoint: lineOrigin, ascent: lineAscent) } else if info.phraseY.contains(i), Let lnHeight = lineAscent + lineDescent + lineLeading lastY -= drawGrips(m: info, lnH: lnHeight, index: i, dB: startIdx, lineOrigin: lineOrigin, context: ctx, lnAscent: LineAscent)} else{// draw CTLineDraw(line, CTX)}Copy the code
github repo
Example 3, word-level control B
For each character, add a border
There are two types of
- A line of characters, all with a border
ShallOmit = true
From each CTLine, get all of its CTRun,
Get all the glyph of CTRun,
The border position of the glyph is the starting position of the line. LineOrigin + the position of the word within the line. Point + the position of the word itself
func drawBorders(context ctx: CGContext, ln line: CTLine, lnOrigin lineOrigin: CGPoint, omit shallOmit: Bool = true) {CTX. SetStrokeColor (UIColor. Green. WithAlphaComponent (0.6). The cgColor) var notOmit = shallOmit / / each line CTRun for run in line.glyphRuns{ if notOmit{ let font = run.font let glyphPositions = run.glyphPositions let glyphs = Glyphs let glyphsBoundingRects = font. BoundingRects (of: glyphs) // Each glyph in CTRun for k in 0.. <glyphPositions.count { let point = glyphPositions[k] var box = glyphsBoundingRects[k] box.origin = box.origin + point + lineOrigin ctx.stroke(box) }// for k } notOmit = true }//for run }Copy the code
- Grid plus one line of words
Add a border to the right line of text
ShallOmit = false, omit the first character border