LoginSignup
14
17

More than 5 years have passed since last update.

iOS 10 から CoreText で背景色が設定できるようになった

Last updated at Posted at 2016-09-13

iOS 9 まで、CoreText の CTLineDraw などで文字を描画するとき、必要あらば手動で別々に背景色を描いてやる必要がありましたが、iOS 10 では、テキストと一緒に背景が描画できるようになっています。

CoreText Changes for Objective-C を見ると、新しく kCTBackgroundColorAttributeName が追加されているのが確認できます。

単純に NSAttributedString を CTTypesetterCreateWithAttributedString に渡して、CTLine を作って描画してみます。

Playground
import UIKit

var attributedText = NSMutableAttributedString(string: "あいうえお", attributes: [
    NSForegroundColorAttributeName: UIColor.red,
    NSBackgroundColorAttributeName: UIColor.blue
])

let typesetter = CTTypesetterCreateWithAttributedString(attributedText)  // NSAttribtedString が CFAttributedString にブリッジして渡される
let ctline = CTTypesetterCreateLine(typesetter, CFRangeMake(0, attributedText.length))

class TestView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.white
    }

    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        context.textPosition = CGPoint(x: 0, y: 20)
        context.translateBy(x: 20, y: 20)
        CTLineDraw(ctline, context)
    }

    required init?(coder aDecoder: NSCoder) {}
}
let testView = TestView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testView

Screen Shot 2016-09-13 at 14.56.13.png

青い背景色が描画されているのが分かります。

弊害と対応策

CoreText (CoreGraphics) と UIKit では座標系が異なる (y軸が逆) ため、ユーザーにとって読みやすくするために、描画対象を上下反転しておく必要があります。(これは今までと同様です)

override func draw(_ rect: CGRect) {
    guard let context = UIGraphicsGetCurrentContext() else { return }
    context.textMatrix = CGAffineTransform(scaleX: 1, y: -1)  // ←テキストを上下反転
    context.textPosition = CGPoint(x: 0, y: 20)
    context.translateBy(x: 20, y: 20) 
    CTLineDraw(ctline, context)
}

こうすると、テキストの文字列は上下反転しますが、テキストの背景は反転しない罠があります😱

Screen Shot 2016-09-13 at 15.01.50.pngScreen Shot 2016-09-13 at 15.01.29.png

背景はテキストと紐付いているので、同じように動く挙動をするのが自然だと思うのですが、とりあえず、context を scaleBy でまるごと反転することで対応できます。

override func draw(_ rect: CGRect) {
    guard let context = UIGraphicsGetCurrentContext() else { return }
    context.textPosition = CGPoint(x: 0, y: -20)
    context.translateBy(x: 20, y: 20)
    context.scaleBy(x: 1, y: -1)
    CTLineDraw(ctline, context)
}

Screen Shot 2016-09-13 at 15.08.25.png

上手に描けました :tada:

14
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
17