11.%d Xcode — UITextView 的 textContainer 的 exclusionPaths

春麗 S.T.E.M.
8 min readAug 3, 2021

--

目錄

⦿ 仍然是貝茲曲線
⦿ TextView、Editable、Selectable
⦿ exclusionPaths
⦿ Debug
⦿ 文繞圖的步驟

仍然是貝茲曲線

這篇大概會是貝茲曲線的最後一篇。

在 TextView 中有個屬性叫做 textContainer,我們先看看,如果你要客製化 TextView,在 programming init 裡會這樣寫:

override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
}

frame 我們都知道是用來定義元件位置跟大小,但在 TextView 裡面還有一個 textContainer,它是用來判定 TextView 裡的 text 是否 render 的。

render 在電腦科學裡的意思是經過處理,text 將過處理後就是顯示的文字,包括字型、字體大小、顯示範圍等等,這裡是文字處理的意思;若在 Adobe Premiere 中,render 就是影像聲音的處理了。

而 textContainer 就是處理文字的容器,它決定你要處理的範圍等等。我們可以參照下面這篇:

接著採用之前這篇用貝茲曲線畫的的楓葉來試試看。

繼續閱讀|回目錄

TextView、Editable、Selectable

先將 textView 加到 ViewController 裡,看看 textView 如下:

在 Attributed Inspector 中,Editable、Selectable 有勾選的話,表示使用者可以在 textView 上編輯,當然也可選取。

這兩項預設都是勾選,不過,若你的 textView 只是用來顯示資訊的話,例如 IMDB 的 APP,可以看到一部電影的詳細內容,這個詳細內容若以 textView 呈現,我們就會取消 Editable。

如果!在顯示頁面需要加上外部連結,一般會這麼做,第一個是取消 Ediable,第二個是勾選 Selectable 如下:

而如果容易辨識的情況下,這些文字 Data 會因為 Detectors 自動辨識出它是電話號碼、連結或是地址⋯⋯。

好了,接下來要回到 TextView 的 textContainer 的 exclusionPaths 了。

繼續閱讀|回目錄

exclusionPaths

在前面,我們已經順利在 TextView 裡加入 UIBezierPath 畫出的楓葉,我們看看什麼是 exclusionPaths,如下:

原來它是一個 path 的陣列,用來表示 text 不會出現的 path,想想在 Office 軟體中操作 Word 時,文繞圖不就是這個意思嗎?而且,這裡的 path 指的就是 UIBezierPath,所以可以想見我們能夠精準控制 UIBezierPath 畫出的楓葉,讓文字環繞著這個楓葉

那麼,先看看 exclusionPaths,程式碼的部份是這樣:

let myPath = createPath()  // 楓葉路徑
myTextView.textContainer.exclusionPaths = [myPath] // text 避開路徑
makeUIView() // 楓葉

再來看看,原先的楓葉是這麼寫的:

func makeUIView() -> UIView {
let redView = UIView(frame: CGRect(x: 0, y: 0,
width: 300, height: 300))
let mapleView = UIView(frame: redView.frame)
mapleView.backgroundColor = UIColor.red
let path = UIBezierPath()
path.move(to: CGPoint(x: 162.87, y: 14.05))
path.addLine(to: CGPoint(x: 150.53, y: 36.54))
path.addLine(to: CGPoint(x: 137.40, y: 29.70))
path.addLine(to: CGPoint(x: 143.47 , y: 67.85))
path.addLine(to: CGPoint(x: 126.06, y: 52.41))
path.addLine(to: CGPoint(x: 120, y: 60))
path.addLine(to: CGPoint(x: 100, y: 60))
path.addLine(to: CGPoint(x: 108.26, y: 78.65))
path.addLine(to: CGPoint(x: 100.26, y: 83.28))
path.addLine(to: CGPoint(x: 132.45, y: 110.39))
path.addLine(to: CGPoint(x: 128.48, y: 122.08))
path.addLine(to: CGPoint(x: 158.68, y: 116.79))
path.addLine(to: CGPoint(x: 158.68, y: 149.42))
path.addLine(to: CGPoint(x: 162.87, y: 149.42))
path.close()
let mapleShapeLayer = CAShapeLayer()
mapleShapeLayer.path = path.cgPath
mapleView.layer.mask = mapleShapeLayer
let moveDistance = path.bounds.maxX * 2
let transform =
CGAffineTransform(translationX: moveDistance, y: 0)
.scaledBy(x: -1, y: 1)
mapleShapeLayer.setAffineTransform(transform) redView.addSubview(mapleView)
redView.center = CGPoint(x: fullScreenSize.width/2,
y: fullScreenSize.height/2)
self.view.addSubview(redView)
return redView
}

所以前一段的 createPath 就是將這裡楓葉的 path 裝進來,並回傳 path,那麼,由於我們在 TextView 裡已裝了許多 text,這時就會自動排開。

結果阿伯出事了!阿伯!

繼續閱讀|回目錄

Debug

看到前段左圖,這位置不對呀!

因為將 Path 拆出來給 textContainer 用,但在畫楓葉的函式中,Path 是先做為 CAShapeLayer 的 Path,最後再將 mapleView 加進 redView,最後再去校準中心點。

所以 redView(含 mapleView)的位置就會跟 Path 不一樣。

那麼,需讓路徑移動,改成如下:

path.apply(CGAffineTransform.identity.scaledBy(x: 1, y: 1).translatedBy(x: 40, y: 202))

最後結果就出現了。

文繞圖的步驟

另外,利用 exclusionPaths 製造出文繞圖的效果,步驟如下:

  1. Textview 中加入 ImageView,設定 imageView 的位置。
  2. 宣告一個 Path 為貝茲曲線函式,大小為 imageView.frame
  3. 再讓 exclusionPaths 的陣列裝入這個路徑

完成了!

這次就分享到這,感謝您的閱讀。

繼續閱讀|回目錄

--

--

春麗 S.T.E.M.
春麗 S.T.E.M.

Written by 春麗 S.T.E.M.

Do not go gentle into that good night, Old age should burn and rave at close of day; Rage, rage, against the dying of the light.

No responses yet