1

I am trying to show Invisible characters using below class & func, and it is working fine.

But replaceGlyphAtIndex is deprecated in OS X 10.11 and we need to use setGlyhs()

setGlyphs(<#T##glyphs: UnsafePointer<CGGlyph>##UnsafePointer<CGGlyph>#>, properties: <#T##UnsafePointer<NSGlyphProperty>#>, characterIndexes: <#T##UnsafePointer<Int>#>, font: <#T##NSFont#>, forGlyphRange: <#T##NSRange#>)

But i am having difficulties on how to convert replaceGlyphAtIndex to setGlyphs. Can any one suggest me how to convert below replaceGlyphAtIndex to setGlyphs()?

I tried as below but it is crashing.

let data = String(g).dataUsingEncoding(NSUTF8StringEncoding)
let cgG = UnsafePointer<CGGlyph>(data!.bytes)
self.setGlyphs(cgG, properties: nil, characterIndexes: UnsafePointer<Int>(bitPattern: characterIndex), font: font as! NSFont, forGlyphRange: NSMakeRange(glyphIndex, 1))

Can any one let me know whats going wrong in the above line of code?

class MyLayoutManager: NSLayoutManager {

override func drawGlyphsForGlyphRange(glyphsToShow: NSRange, atPoint origin: NSPoint) {
    if let storage = self.textStorage {
        let s = storage.string
        let startIndex = s.startIndex

        for glyphIndex in glyphsToShow.location ..< glyphsToShow.location + glyphsToShow.length {
            let characterIndex = self.characterIndexForGlyphAtIndex(glyphIndex)
            let ch = s[startIndex.advancedBy(characterIndex)]
            switch ch {
            case " ": //Blank Space
                let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil)
                if let font = attrs[NSFontAttributeName] {
                    let g = font.glyphWithName("period")//("periodcentered")
                    self.replaceGlyphAtIndex(glyphIndex, withGlyph: g)


                    //                        let data = String(g).dataUsingEncoding(NSUTF8StringEncoding)
                    //                        let cgG = UnsafePointer<CGGlyph>(data!.bytes)
                    //
                    //                        self.setGlyphs(cgG, properties: nil, characterIndexes: UnsafePointer<Int>(bitPattern: characterIndex), font: font as! NSFont, forGlyphRange: NSMakeRange(glyphIndex, 1))
                }
            case "\n": //New Line
                let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil)
                if let font = attrs[NSFontAttributeName] {
                    let g = font.glyphWithName("logicalnot")
                    self.replaceGlyphAtIndex(glyphIndex, withGlyph: g)
                }
            case newLineUniCodeStr:
                let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil)
                if let font = attrs[NSFontAttributeName] {
                    let g = font.glyphWithName("logicalnot")
                    self.replaceGlyphAtIndex(glyphIndex, withGlyph: g)
                }
            default:
                break
            }
        }
    }
    super.drawGlyphsForGlyphRange(glyphsToShow, atPoint: origin)
}

}

Shiva Kumar
  • 389
  • 3
  • 22

1 Answers1

2

I found another way to display Glyphs. (Posting my code below as it may be useful to others)

override func drawGlyphsForGlyphRange(glyphsToShow: NSRange, atPoint origin: NSPoint) {
    if let storage = self.textStorage {
        let s = storage.string
        let startIndex = s.startIndex

        var padding:CGFloat = 0
        for glyphIndex in glyphsToShow.location ..< glyphsToShow.location + glyphsToShow.length {
            let characterIndex = self.characterIndexForGlyphAtIndex(glyphIndex)
            if characterIndex < s.characters.count
            {
                var glyphStr = ""
                let ch = s[startIndex.advancedBy(characterIndex)]
                switch ch {
                case " ": //Blank Space
                    glyphStr = periodCenteredUniCodeStr
                case "\n": //New Line
                    glyphStr = lineBreakUniCodeStr
                case newLineUniCodeStr:
                    glyphStr = lineBreakUniCodeStr
                    padding += 5
                default:
                    break
                }
                var glyphPoint = self.locationForGlyphAtIndex(glyphIndex)
                let glyphRect = self.lineFragmentRectForGlyphAtIndex(glyphIndex, effectiveRange: nil)
                if glyphStr.characters.count > 0{
                    glyphPoint.x = glyphPoint.x + glyphRect.origin.x
                    glyphPoint.y = glyphRect.origin.y
                    NSString(string: glyphStr).drawInRect(NSMakeRect(glyphPoint.x, glyphPoint.y, 10, 10), withAttributes: nil)
                }
            }else{
                print("Wrong count here")
            }
        }

    }
    super.drawGlyphsForGlyphRange(glyphsToShow, atPoint: origin)
}
Shiva Kumar
  • 389
  • 3
  • 22
  • Unfortunately, this way is too slow and won't work with multi page layout – Vitalii Vashchenko Oct 03 '16 at 19:29
  • @VitaliyVashchenko yes i recently did noticed that. Do you know any better idea? So that it will he helpful for me and others. – Shiva Kumar Oct 04 '16 at 08:33
  • Yes, it's extremely efficient. When I found the solution I have posted code in my question topic here: http://stackoverflow.com/questions/39545718/nslayoutmanager-hides-new-line-characters-no-matter-what-i-do – Vitalii Vashchenko Oct 04 '16 at 14:32
  • But your code could be adopted to the multi page layout very easily. Just get the textContainer by characterIndex and you'll get a correct text view object. Then just add CATextLayer sublayers with control characters into you text view. Your text view should be layer-backed in that case. But it's really gets a good performance too. But you should manually remove those sublayers when layout manager invalidates layout and check for duplicates before adding a sublayer. – Vitalii Vashchenko Oct 04 '16 at 14:36
  • @VitaliyVashchenko your code is alot better than mine in terms of performance (setGlyphs()), but it is in Swift3, can you convert it to swift 2.3? Main problem for me is in Swift 2.3 "MemoryLayout" is not available. – Shiva Kumar Oct 06 '16 at 06:41
  • Sorry, Xcode 8 can't convert from 3 to 2.3 :(. But indstead of MemoryLayout try sizeOf: let glyphsRef = UnsafeMutablePointer(malloc(sizeof(CGGlyph.self) * count)) – Vitalii Vashchenko Oct 06 '16 at 07:07