Search code examples
swiftmacoscore-textstrikethrough

Strikethrough not working in CoreText on macOS


Strikethrough is not being displayed, but underline does. The code is as below, its fairly straight forward. When I comment out the underline the text is displayed without a strikethrough, when I comment out the strikethrough and display the underline it works fine. I've tried everything — I must be missing something obvious, the docs say strikeout should work.

I'm running macOS 10.13.6 and Xcode 10.1.

import AppKit
import PlaygroundSupport

class CustomView: NSView {
    override func draw(_ dirtyRect: NSRect) {
        let attrString = NSAttributedString(
            string: "Hello",
            attributes: [
                //NSAttributedString.Key.underlineStyle: NSUnderlineStyle.thick.rawValue,
                NSAttributedString.Key.strikethroughStyle: NSUnderlineStyle.thick.rawValue
            ]
        )
        let line = CTLineCreateWithAttributedString(attrString)

        // Set text position and draw the line into the graphics context.
        let context = (NSGraphicsContext.current?.cgContext)!
        context.translateBy(x: 10, y: 10)
        CTLineDraw(line, context)
    }
}

let customView = CustomView(frame: NSRect(x: 0, y: 0, width: 400, height: 400))

PlaygroundPage.current.liveView = customView

Playground settings. To run this in a Xcode Playground just be sure to change the platform to macOS in the inspector settings (all new playgrounds are set to iOS by default).


Solution

  • CoreText does not have native support for the strikethrough typographic text decoration (but supports underlines just fine as you noted in your question). The full list of string attributes supported by CoreText is described here.

    This old, Objective-C based blog post provides the necessary details to implement strikethrough manually using CoreText APIs. Unfortunately, not a quick fix at all.


    The higher-level TextKit framework does provide strikethrough support though. To quickly see that working just replace this line in your original code:

    CTLineDraw(line, context)
    

    with this instead:

    attrString.draw(at: .zero)
    

    And, by the way, that somehow helps validate that there was nothing wrong with your original NSAttributedString to begin with ;)

    Of course, depending on how much your code relies on CoreText, switching over to TextKit might be a non-trivial task, so you need to keep that in mind as well.