From 722a7b4759b85689e69f1a8ae6c8ab809986f638 Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Thu, 9 Nov 2017 15:05:57 -0300 Subject: [PATCH 1/4] TextStorageTests: New Unit Test --- AztecTests/TextKit/TextStorageTests.swift | 61 +++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/AztecTests/TextKit/TextStorageTests.swift b/AztecTests/TextKit/TextStorageTests.swift index 637eb4d73..77fa3ccd0 100644 --- a/AztecTests/TextKit/TextStorageTests.swift +++ b/AztecTests/TextKit/TextStorageTests.swift @@ -17,6 +17,11 @@ class TextStorageTests: XCTestCase { /// var mockDelegate: MockAttachmentsDelegate! + /// Default Text Attributes + /// + let defaultAttributes: [NSAttributedStringKey: Any] = [.foregroundColor: UIColor.black, + .font: UIFont.systemFont(ofSize: 14), + .paragraphStyle: ParagraphStyle.default] override func setUp() { super.setUp() @@ -381,9 +386,6 @@ class TextStorageTests: XCTestCase { let commentString = "This is a comment" let html = "" - let defaultAttributes: [NSAttributedStringKey: Any] = [.font: UIFont.systemFont(ofSize: 14), - .paragraphStyle: ParagraphStyle.default] - storage.setHTML(html, defaultAttributes: defaultAttributes) storage.replaceCharacters(in: NSRange(location: 0, length: 1), with: NSAttributedString(string: "")) @@ -391,4 +393,57 @@ class TextStorageTests: XCTestCase { XCTAssertEqual(String(), resultHTML) } + + /// This test verifies that, whenever a NSAttributedString is inserted inline (and has a different font), the ParagraphStyle + /// attribute will be 'fixed'. This translates into: there won't be different instances of ParagraphStyle for characters in the same line. + /// This has been tracked to be causing display issues when using the `Chinese (Simplified) Pinyin` keyboard. + /// + /// Reference: https://github.com/wordpress-mobile/AztecEditor-iOS/issues/811 + /// + func testAttributesAreFixedWheneverStringsWithDifferentAttributesAreInsertedOnTheSameLine() { + + /// Attributes + /// + let formatterH1 = HeaderFormatter(headerLevel: .h1) + let headerAttributes = formatterH1.apply(to: defaultAttributes, andStore: nil) + + /// Precondition: Newline at the top + /// + let newlineString = NSAttributedString(string: "\n", attributes: defaultAttributes) + + storage.replaceCharacters(in: .zero, with: newlineString) + + /// Insert + Replace: with H1 Attributes + /// + let insertionRange1 = NSRange(location: 1, length: 0) + let characterRange1 = NSRange(location: 1, length: 1) + + let chineseStringH1 = NSAttributedString(string: "上", attributes: headerAttributes) + let regularStringH1 = NSAttributedString(string: "s", attributes: headerAttributes) + + storage.replaceCharacters(in: insertionRange1, with: regularStringH1) + storage.replaceCharacters(in: characterRange1, with: chineseStringH1) + + /// After the two calls above, when typing, TextView will not relay properly apply the `H1` attributes. We'll simulate that: + /// + let chineseStringNormal = NSAttributedString(string: "上", attributes: defaultAttributes) + let regularStringNormal = NSAttributedString(string: "s", attributes: defaultAttributes) + + let insertionRange2 = NSRange(location: 2, length: 0) + let characterRange2 = NSRange(location: 2, length: 1) + + storage.replaceCharacters(in: insertionRange2, with: regularStringNormal) + storage.replaceCharacters(in: characterRange2, with: chineseStringNormal) + + /// Context: + /// storage.string at this point contains "\n上上" + /// + /// PROBLEM: + /// If characters 1 and 2 have different paragraphStyles, the second character may not get properly displayed by the TextView. + /// + let paragraphStyle1 = storage.attribute(.paragraphStyle, at: characterRange1.location, effectiveRange: nil) as! NSParagraphStyle + let paragraphStyle2 = storage.attribute(.paragraphStyle, at: characterRange2.location, effectiveRange: nil) as! NSParagraphStyle + + XCTAssertEqual(paragraphStyle1, paragraphStyle2) + } } From f158a1d1ecf9ba6f52b850b476a20f69c99a8882 Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Thu, 9 Nov 2017 15:06:15 -0300 Subject: [PATCH 2/4] TextStorage: Fixing Attributes upon text replacement --- Aztec/Classes/TextKit/TextStorage.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Aztec/Classes/TextKit/TextStorage.swift b/Aztec/Classes/TextKit/TextStorage.swift index 61d83d6e3..64f4e04ac 100644 --- a/Aztec/Classes/TextKit/TextStorage.swift +++ b/Aztec/Classes/TextKit/TextStorage.swift @@ -254,6 +254,11 @@ open class TextStorage: NSTextStorage { textStore.replaceCharacters(in: range, with: preprocessedString) edited([.editedAttributes, .editedCharacters], range: range, changeInLength: attrString.length - range.length) + if range.length > 0 { + let invalidateRange = NSMakeRange(range.location, attrString.length) + fixAttributes(in: invalidateRange) + } + endEditing() } From 15f9db6ecba0d6ac0bf846afbc9a0ea9f477ebb2 Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Fri, 10 Nov 2017 10:25:56 -0300 Subject: [PATCH 3/4] TextStorage: Adding comment --- Aztec/Classes/TextKit/TextStorage.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Aztec/Classes/TextKit/TextStorage.swift b/Aztec/Classes/TextKit/TextStorage.swift index 64f4e04ac..2b8171005 100644 --- a/Aztec/Classes/TextKit/TextStorage.swift +++ b/Aztec/Classes/TextKit/TextStorage.swift @@ -254,6 +254,11 @@ open class TextStorage: NSTextStorage { textStore.replaceCharacters(in: range, with: preprocessedString) edited([.editedAttributes, .editedCharacters], range: range, changeInLength: attrString.length - range.length) + // Whenever we're actually replacing text, let's trigger a `fixAttributes` call. This is done to prevent a glitch in which + // TextView may not render characters, in the same line, that carry partitioned attributes. + // + // Ref. https://github.com/wordpress-mobile/AztecEditor-iOS/issues/811 + // if range.length > 0 { let invalidateRange = NSMakeRange(range.location, attrString.length) fixAttributes(in: invalidateRange) From 000ecbc0211f2641b9b9fae71c7885b583d7fa41 Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Thu, 23 Nov 2017 19:14:52 -0300 Subject: [PATCH 4/4] TextStorage: Attribute Invalidation upon replace --- Aztec/Classes/TextKit/TextStorage.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Aztec/Classes/TextKit/TextStorage.swift b/Aztec/Classes/TextKit/TextStorage.swift index f7b438281..1d25aa39f 100644 --- a/Aztec/Classes/TextKit/TextStorage.swift +++ b/Aztec/Classes/TextKit/TextStorage.swift @@ -274,7 +274,7 @@ open class TextStorage: NSTextStorage { // if range.length > 0 { let invalidateRange = NSMakeRange(range.location, attrString.length) - fixAttributes(in: invalidateRange) + invalidateAttributes(in: invalidateRange) } endEditing()