2016-09-17 5 views
1

NSTextView 하위 클래스의 줄 바꿈 문자와 같은 보이지 않는 문자를 표시하려고합니다. NSLayoutManager의 drawGlyph 메서드를 재정의하는 것과 같은 일반적인 방법은 너무 느리고 다중 페이지 레이아웃에서 제대로 작동하지 않기 때문에 좋지 않은 아이디어입니다.NSLayoutManager는 내가하는 일에 상관없이 줄 바꿈 문자를 숨 깁니다.

NSLayoutManager의 setGlyph 메서드를 재정의하여 보이지 않는 "\ n"글리프를 "¶"글리프로, ""를 "∙"로 바꿉니다.

그리고 공간 글리프에서는 작동하지만 새 줄 문자에는 영향을 미치지 않습니다.

public override func setGlyphs(_ glyphs: UnsafePointer<CGGlyph>, properties props: UnsafePointer<NSGlyphProperty>, characterIndexes charIndexes: UnsafePointer<Int>, font aFont: Font, forGlyphRange glyphRange: NSRange) { 
    var substring = (self.currentTextStorage.string as NSString).substring(with: glyphRange) 

    // replace invisible characters with visible 
    if PreferencesManager.shared.shouldShowInvisibles == true { 
     substring = substring.replacingOccurrences(of: " ", with: "\u{00B7}") 
     substring = substring.replacingOccurrences(of: "\n", with: "u{00B6}") 
    } 

    // create a CFString 
    let stringRef = substring as CFString 
    let count = CFStringGetLength(stringRef) 

    // convert processed string to the C-pointer 
    let cfRange = CFRangeMake(0, count) 
    let fontRef = CTFontCreateWithName(aFont.fontName as CFString?, aFont.pointSize, nil) 
    let characters = UnsafeMutablePointer<UniChar>.allocate(capacity: MemoryLayout<UniChar>.size * count) 
    CFStringGetCharacters(stringRef, cfRange, characters) 

    // get glyphs for the pointer of characters 
    let glyphsRef = UnsafeMutablePointer<CGGlyph>.allocate(capacity: MemoryLayout<CGGlyph>.size * count) 
    CTFontGetGlyphsForCharacters(fontRef, characters, glyphsRef, count) 

    // set those glyphs 
    super.setGlyphs(glyphsRef, properties:props, characterIndexes: charIndexes, font: aFont, forGlyphRange: glyphRange) 
} 

그런 다음 나는 아이디어를 내놓았다 : NSTypesetter는 전혀 처리하지 말아야 것과 같은 새로운 라인 문자 범위를 표시 것 같습니다. 따라서 NSTypesetter를 서브 클래 싱하여 메서드를 재정의했습니다.

override func setNotShownAttribute(_ flag: Bool, forGlyphRange glyphRange: NSRange) { 
    let theFlag = PreferencesManager.shared.shouldShowInvisibles == true ? false : true 
    super.setNotShownAttribute(theFlag, forGlyphRange: glyphRange) 
} 

그러나 작동하지 않습니다. NSLayoutManager는 내가 만든 글립이 무엇이든 새 라인 문자에 대한 글리프를 생성하지 않습니다.

내가 뭘 잘못하고 있니?

답변

1

NSTypesetter의 setNotShownAttribute :의 기본 구현은 이미 글리프 저장소에서 생성 된 글리프를 변경하지 않습니다. 그래서 슈퍼 콜이 아무런 효과를 내지 못합니다. 슈퍼를 호출하기 전에 수동으로 글리프를 수동으로 바꾸기 만하면됩니다. 앱이 텍스트보기에 여러 글꼴을 가지고있는 경우 :이 방법의

제한 :

그래서, 눈에 보이지 않는 문자를 보여주는 가장 효율적인 구현 (보기를 확대하면서 차이를 볼 것)이있다 ,이 접근법은 좋은 생각이 아닐 수도 있습니다. 보이지 않는 문자로 표시된 글꼴의 글꼴도 다를 수 있기 때문입니다. 그리고 그것은 당신이 달성하기를 원하는 것이 아닙니다.

public override func setGlyphs(_ glyphs: UnsafePointer<CGGlyph>, properties props: UnsafePointer<NSGlyphProperty>, characterIndexes charIndexes: UnsafePointer<Int>, font aFont: Font, forGlyphRange glyphRange: NSRange) { 
    var substring = (self.currentTextStorage.string as NSString).substring(with: glyphRange) 

    // replace invisible characters with visible 
    if PreferencesManager.shared.shouldShowInvisibles == true { 
     substring = substring.replacingOccurrences(of: " ", with: "\u{00B7}") 
    } 

    // create a CFString 
    let stringRef = substring as CFString 
    let count = CFStringGetLength(stringRef) 

    // convert processed string to the C-pointer 
    let cfRange = CFRangeMake(0, count) 
    let fontRef = CTFontCreateWithName(aFont.fontName as CFString?, aFont.pointSize, nil) 
    let characters = UnsafeMutablePointer<UniChar>.allocate(capacity: MemoryLayout<UniChar>.size * count) 
    CFStringGetCharacters(stringRef, cfRange, characters) 

    // get glyphs for the pointer of characters 
    let glyphsRef = UnsafeMutablePointer<CGGlyph>.allocate(capacity: MemoryLayout<CGGlyph>.size * count) 
    CTFontGetGlyphsForCharacters(fontRef, characters, glyphsRef, count) 

    // set those glyphs 
    super.setGlyphs(glyphsRef, properties:props, characterIndexes: charIndexes, font: aFont, forGlyphRange: glyphRange) 
} 
  • 서브 클래스 NSATSTypesetter을하고 NSLayoutManager의 subclas에 할당 :

    1. 서브 클래스 NSLayoutManager 및 오버라이드 (override) setGlyphs 공간의 문자를 표시합니다. 단지 전화

      class CustomTypesetter: NSATSTypesetter { 
      
          override func setNotShownAttribute(_ flag: Bool, forGlyphRange glyphRange: NSRange) { 
           var theFlag = flag 
      
           if PreferencesManager.shared.shouldShowInvisibles == true { 
            theFlag = false 
      
            // add new line glyphs into the glyph storage 
            var newLineGlyph = yourFont.glyph(withName: "paragraph") 
            self.substituteGlyphs(in: glyphRange, withGlyphs: &newLineGlyph) 
      
            // draw new line char with different color 
            self.layoutManager?.addTemporaryAttribute(NSForegroundColorAttributeName, value: NSColor.invisibleTextColor, forCharacterRange: glyphRange) 
           } 
      
           super.setNotShownAttribute(theFlag, forGlyphRange: glyphRange) 
          } 
      
          /// Currently hadn't found any faster way to draw space glyphs with different color 
          override func setParagraphGlyphRange(_ paragraphRange: NSRange, separatorGlyphRange paragraphSeparatorRange: NSRange) { 
           super.setParagraphGlyphRange(paragraphRange, separatorGlyphRange: paragraphSeparatorRange) 
      
           guard PreferencesManager.shared.shouldShowInvisibles == true else { return } 
      
           if let substring = (self.layoutManager?.textStorage?.string as NSString?)?.substring(with: paragraphRange) { 
            let expression = try? NSRegularExpression.init(pattern: "\\s", options: NSRegularExpression.Options.useUnicodeWordBoundaries) 
            let sunstringRange = NSRange(location: 0, length: substring.characters.count) 
      
            if let matches = expression?.matches(in: substring, options: NSRegularExpression.MatchingOptions.withoutAnchoringBounds, range: sunstringRange) { 
             for match in matches { 
              let globalSubRange = NSRange(location: paragraphRange.location + match.range.location, length: 1) 
              self.layoutManager?.addTemporaryAttribute(NSForegroundColorAttributeName, value: Color.invisibleText, forCharacterRange: globalSubRange) 
             } 
            } 
           } 
          } 
      } 
      
    2. 가 표시하려면/보이지 않는 문자를 숨기기 : 서브 클래스는 새로운 라인 문자를 표시하고 모든 눈에 보이지 않는 문자가 다른 색으로 그려집니다 있는지 확인합니다

      let storageRange = NSRange(location: 0, length: currentTextStorage.length) 
      layoutManager.invalidateGlyphs(forCharacterRange: storageRange, changeInLength: 0, actualCharacterRange: nil) 
      layoutManager.ensureGlyphs(forGlyphRange: storageRange) 
      
  • +0

    이 보인다 이 코드는 Swift3에 있습니다. Swift 2.3에서 어떻게 변환 할 수 있습니까? Swift 2.3에서는 "MemoryLayout"을 사용할 수 없습니다. –