From 807eed1c8cc069d00217f78fb815351f84ff3839 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Fri, 20 Dec 2024 12:29:13 -1000 Subject: [PATCH] Format text with TextKit2 content storage methods --- Simplenote/SPTextView.swift | 75 +++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/Simplenote/SPTextView.swift b/Simplenote/SPTextView.swift index 86101ee0f..a34315b24 100644 --- a/Simplenote/SPTextView.swift +++ b/Simplenote/SPTextView.swift @@ -7,17 +7,6 @@ // extension SPTextView { - /* - SPInteractiveTextStorage *textStorage = [[SPInteractiveTextStorage alloc] init]; - NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; - - NSTextContainer *container = [[NSTextContainer alloc] initWithSize:CGSizeMake(0, CGFLOAT_MAX)]; - container.widthTracksTextView = YES; - container.heightTracksTextView = YES; - [layoutManager addTextContainer:container]; - [textStorage addLayoutManager:layoutManager]; - */ - @objc func setupTextContainer(with textStorage: SPInteractiveTextStorage) -> NSTextContainer { let container = NSTextContainer(size: .zero) @@ -28,6 +17,7 @@ extension SPTextView { if #available(iOS 16.0, *) { let textLayoutManager = NSTextLayoutManager() let contentStorage = NSTextContentStorage() + contentStorage.delegate = self contentStorage.addTextLayoutManager(textLayoutManager) textLayoutManager.textContainer = container @@ -39,3 +29,66 @@ extension SPTextView { return container } } + +// MARK: NSTextContentStorageDelegate +// +extension SPTextView: NSTextContentStorageDelegate { + public func textContentStorage(_ textContentStorage: NSTextContentStorage, textParagraphWith range: NSRange) -> NSTextParagraph? { + guard let originalText = textContentStorage.textStorage?.attributedSubstring(from: range) as? NSMutableAttributedString else { + return nil + } + + let style = textInRangeIsHeader(range) ? headlineStyle : defaultStyle + originalText.addAttributes(style, range: originalText.fullRange) + + return NSTextParagraph(attributedString: originalText) + } + + func textInRangeIsHeader(_ range: NSRange) -> Bool { + range.location == .zero + } + + // MARK: Styles + // + var headlineFont: UIFont { + UIFont.preferredFont(for: .title1, weight: .bold) + } + + var defaultFont: UIFont { + UIFont.preferredFont(forTextStyle: .body) + } + + var defaultTextColor: UIColor { + UIColor.simplenoteNoteHeadlineColor + } + + var lineSpacing: CGFloat { + defaultFont.lineHeight * Metrics.lineSpacingMultipler + } + + var defaultStyle: [NSAttributedString.Key: Any] { + [ + .font: defaultFont, + .foregroundColor: defaultTextColor, + .paragraphStyle: NSMutableParagraphStyle(lineSpacing: lineSpacing) + ] + } + + var headlineStyle: [NSAttributedString.Key: Any] { + [ + .font: headlineFont, + .foregroundColor: defaultTextColor, + ] + } +} + +// MARK: - Metrics +// +private enum Metrics { + static let lineSpacingMultiplerPad: CGFloat = 0.40 + static let lineSpacingMultiplerPhone: CGFloat = 0.20 + + static var lineSpacingMultipler: CGFloat { + UIDevice.isPad ? lineSpacingMultiplerPad : lineSpacingMultiplerPhone + } +}