Внешний вид противоречит коду, в Simple AttributedString NSParagraphStyle отображается в UITextView

Я пытаюсь вручную проанализировать строку уценки и создать атрибутную строку для UITextView.

Когда я пытаюсь форматировать блоки кода, я обнаружил странную ошибку, которую не могу решить. Я сократил проблему до этого минимального количества демонстрационного кода. Я надеюсь, что кто-то может мне помочь.

Проблема:

Все блоки имеют одинаковый стиль. Но одни и те же атрибуты приводят к разному внешнему виду, что видно после первых трех обратных кавычек в каждом блоке кода.

Первый блок работает нормально. Но в приведенных ниже есть этот странный дополнительный пробел между первой строкой и второй строкой.

3 блока стилизованы с использованием цикла for с идентичным кодом.

Код

Я сократил код и поместил его в один файл быстрой игровой площадки. И я очень стараюсь сделать пост легким для чтения и понимания, чтобы мне могли помочь.

Код прямого стиля занимает менее 10 строк, может быть, 7 строк.

Я разделил код на 3 части в своем посте для удобства чтения. Но их можно собрать напрямую и запустить на быстрой игровой площадке.

      //: A UIKit based Playground for presenting user interface
  
import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {
    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        let str = """
`` ` 
some
`` `

`` `
some
`` `

`` `
some
`` ` <- (conflicting with stackoverflow markdown editor, so i added extra space)
"""
        var at = AttributedString(stringLiteral: str)

    /* === ============= === */
    /* === styling start === */
    /* === ↓ ↓ ↓ ↓ ↓ ↓ ↓ === */

        // manually counted range and indexes for 3 code blocks
        for range in [at.intToRange(start: 0, end: 12),
                      at.intToRange(start: 14, end: 26),
                      at.intToRange(start: 28, end: 40)] {
            /* apply code block attributes style */
            at[range].mergeAttributes(.codeBlock)

            // "lower" is the range of head 3 `
            let lower = range.lowerBound..<at.index(range.lowerBound, offsetByCharacters: 3)

            // "upper" is the range of tail 3 `
            let upper = at.index(range.upperBound, offsetByCharacters: -3)..<range.upperBound

            /* apply code block prefix ` and suffix ` attributes */
            at[lower].mergeAttributes(.codeBlockPrefixBacktick)
            at[upper].mergeAttributes(.codeBlockSuffixBacktick)
            
    // still in loop

    /* === ↑ ↑ ↑ ↑ ↑ ↑ === */
    /* === styling end === */
    /* === =========== === */

            /* print out prefix and suffix paragraph attributes */
            /* ↓ this part has nothing to do with styling ↓ */
            [lower, upper].forEach {
                for i in 0..<3 {
                    let a = at.index($0.lowerBound, offsetByCharacters: i)
                    let b = at.index($0.lowerBound, offsetByCharacters: i+1)
                    let c = at[a..<b]
                    print(c.characters[a])
                    print(c[AttributeScopes.UIKitAttributes.ParagraphStyleAttribute])
                }
            }
        }


        /* Give the AttributedString To UITextView and display */
        let tv2 = UITextView()
        tv2.isScrollEnabled = true
        tv2.frame = CGRectMake(0, 0, 600, 800)
        tv2.attributedText = NSAttributedString(at)
        
        view.addSubview(tv2)
        self.view = view
        
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

Предустановки стиля : 3 основных компонента: шрифты, отступы и интервалы.

      extension AttributeContainer {

    // code block main body style

    static let codeBlock = AttributeContainer
        .font(UIFont(name: "Menlo", size: 15)!)
        .paragraphStyle(AttributeContainer.codeBlockStyle)
        .backgroundColor(UIColor.lightGray)
    
    
    private static let codeBlockStyle:NSParagraphStyle = {
        let style = NSMutableParagraphStyle()
        style.headIndent = 12
        style.firstLineHeadIndent = 12
        style.tailIndent = 12
        return style
    }()
    

    // code block prefix style

    static let codeBlockPrefixBacktick = AttributeContainer
        .font(UIFont(name: "Menlo", size: 17)!)
        .paragraphStyle(AttributeContainer.codeBlockPrefixStyle)
        .backgroundColor(UIColor.lightGray)

    
    private static let codeBlockPrefixStyle:NSParagraphStyle = {
        let style = NSMutableParagraphStyle()
        style.headIndent = 12
        style.firstLineHeadIndent = 12
        style.tailIndent = 12
        style.paragraphSpacingBefore = 24
        style.paragraphSpacing = 0
        return style
    }()
    
    
    // code block suffix style
    
    static let codeBlockSuffixBacktick = AttributeContainer
        .font(UIFont(name: "Menlo", size: 17)!)
        .paragraphStyle(AttributeContainer.codeBlockSuffixStyle)
        .backgroundColor(UIColor.lightGray)

    static let codeBlockSuffixStyle:NSParagraphStyle = {
        let style = NSMutableParagraphStyle()
        style.headIndent = 12
        style.firstLineHeadIndent = 12
        style.tailIndent = 12
        style.paragraphSpacingBefore = 0
        style.paragraphSpacing = 24
        return style
    }()
}

Некоторые удобные функции

      extension AttributedString {

    /// convert ints to range
    func intToRange(start:Int, end:Int) -> Range<Index> {
        let lower = convertIntToIndex(int: start)
        let upper = convertIntToIndex(int: end)
        return lower..<upper
    }
    
    /// convert Int to String.Index
    func convertIntToIndex(int:Int) -> Index {
        characters.index(startIndex, offsetBy: int)
    }
    
    /// get character at index i
    /// string[i]
    subscript(i:Index) -> Character {
        return characters[i]
    }
}

Я считаю, что стили этих трех блоков идентичны, потому что я использовал только цикл for с идентичным кодом.

Когда я попытался распечатать их стиль, проблем тоже не возникло. Похоже, внешний вид противоречит коду.

Вот напечатанный результат каждой отдельной обратной кавычки (`), всего 18, посмотрите наиатрибуты. Они кажутся прекрасными.

      `
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 24, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 24, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 24, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 24, ParagraphSpacingBefore 0, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (
), Lists (
), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 24, ParagraphSpacingBefore 0, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (
), Lists (
), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 24, ParagraphSpacingBefore 0, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (
), Lists (
), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 24, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 24, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 24, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 24, ParagraphSpacingBefore 0, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (
), Lists (
), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 24, ParagraphSpacingBefore 0, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (
), Lists (
), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 24, ParagraphSpacingBefore 0, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (
), Lists (
), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 24, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 24, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 24, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 24, ParagraphSpacingBefore 0, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (
), Lists (
), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 24, ParagraphSpacingBefore 0, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (
), Lists (
), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '')
`
Optional(Alignment 4, LineSpacing 0, ParagraphSpacing 24, ParagraphSpacingBefore 0, HeadIndent 12, TailIndent 12, FirstLineHeadIndent 12, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
    28L,
    56L,
    84L,
    112L,
    140L,
    168L,
    196L,
    224L,
    252L,
    280L,
    308L,
    336L
), DefaultTabInterval 0, Blocks (
), Lists (
), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (
) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '')

Заранее спасибо, если кто поможет.

0 ответов

Другие вопросы по тегам