From f15e3134d78defeba28636f5a8eafed626099c5a Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Fri, 20 Dec 2024 13:01:58 -1000 Subject: [PATCH] Update text view gesture recognizer to use textkit 2 --- Simplenote.xcodeproj/project.pbxproj | 8 ++++++++ .../UIGestureRecognizer+Simplenote.swift | 14 +++++++++++--- Simplenote/NSTextLayoutFragment.swift | 17 +++++++++++++++++ Simplenote/NSTextLayoutManager+Simplenote.swift | 17 +++++++++++++++++ Simplenote/SPTextView.swift | 2 +- 5 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 Simplenote/NSTextLayoutFragment.swift create mode 100644 Simplenote/NSTextLayoutManager+Simplenote.swift diff --git a/Simplenote.xcodeproj/project.pbxproj b/Simplenote.xcodeproj/project.pbxproj index d74bfac6e..605d06472 100644 --- a/Simplenote.xcodeproj/project.pbxproj +++ b/Simplenote.xcodeproj/project.pbxproj @@ -541,6 +541,8 @@ BAB6C04726BA4CAF007495C4 /* WidgetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB6C04626BA4CAF007495C4 /* WidgetController.swift */; }; BAB898D32BEC404200E238B8 /* CreateNewNoteIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB898D22BEC404200E238B8 /* CreateNewNoteIntentHandler.swift */; }; BABB22DF2D14DA6600FCF47D /* SPTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BABB22DE2D14DA6300FCF47D /* SPTextView.swift */; }; + BABB22E12D162E6D00FCF47D /* NSTextLayoutManager+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BABB22E02D162E6200FCF47D /* NSTextLayoutManager+Simplenote.swift */; }; + BABB22E32D162E8400FCF47D /* NSTextLayoutFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = BABB22E22D162E7D00FCF47D /* NSTextLayoutFragment.swift */; }; BABFFF2226CF9094003A4C25 /* WidgetDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = BABFFF2126CF9094003A4C25 /* WidgetDefaults.swift */; }; BABFFF2326CF9094003A4C25 /* WidgetDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = BABFFF2126CF9094003A4C25 /* WidgetDefaults.swift */; }; BABFFF2426CF9094003A4C25 /* WidgetDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = BABFFF2126CF9094003A4C25 /* WidgetDefaults.swift */; }; @@ -1244,6 +1246,8 @@ BAB6C04626BA4CAF007495C4 /* WidgetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetController.swift; sourceTree = ""; }; BAB898D22BEC404200E238B8 /* CreateNewNoteIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateNewNoteIntentHandler.swift; sourceTree = ""; }; BABB22DE2D14DA6300FCF47D /* SPTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPTextView.swift; sourceTree = ""; }; + BABB22E02D162E6200FCF47D /* NSTextLayoutManager+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextLayoutManager+Simplenote.swift"; sourceTree = ""; }; + BABB22E22D162E7D00FCF47D /* NSTextLayoutFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextLayoutFragment.swift; sourceTree = ""; }; BABFFF2126CF9094003A4C25 /* WidgetDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetDefaults.swift; sourceTree = ""; }; BAD0F1EC2BED49C200E73E45 /* FindNoteIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindNoteIntentHandler.swift; sourceTree = ""; }; BAE08625261282D1009D40CD /* Note+Publish.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Note+Publish.swift"; sourceTree = ""; }; @@ -1879,6 +1883,8 @@ E21F57B817C1244E001F02D3 /* SPEditorTextView.m */, A6BBDA45255034E6005C8343 /* SPEditorTextView+Simplenote.swift */, B52E2B142537480A0074509A /* SPEditorTapRecognizerDelegate.swift */, + BABB22E02D162E6200FCF47D /* NSTextLayoutManager+Simplenote.swift */, + BABB22E22D162E7D00FCF47D /* NSTextLayoutFragment.swift */, ); name = Editor; sourceTree = ""; @@ -3471,6 +3477,7 @@ A60DF30825A44F0F00FDADF3 /* PinLockRemoveController.swift in Sources */, A694ABAB25D1549D00CC3A2D /* FileStorage.swift in Sources */, B513FB2422EF6A4B00B178AC /* SPUserInterface.swift in Sources */, + BABB22E32D162E8400FCF47D /* NSTextLayoutFragment.swift in Sources */, B5DF734022A54EE800602CE7 /* SPDefaultTableViewCell.swift in Sources */, B5796549250684EC00DFD6F7 /* EditorFactory.swift in Sources */, A6C2721825AF0C1E00593731 /* TagListViewCell.swift in Sources */, @@ -3542,6 +3549,7 @@ A6CDF900256B9CB900CF2F27 /* ViewSpinner.swift in Sources */, 375D24B721E01131007AB25A /* stack.c in Sources */, 373AD30821C4739500A4EA89 /* NSMutableAttributedString+Styling.m in Sources */, + BABB22E12D162E6D00FCF47D /* NSTextLayoutManager+Simplenote.swift in Sources */, B524AE112352CC7900EA11D4 /* UIScreen+Simplenote.swift in Sources */, A68A4345256FEE3000D1CA5D /* SPSettingsViewController+Extensions.swift in Sources */, B543C7E523CF775C00003A80 /* NSSortDescriptor+Simplenote.swift in Sources */, diff --git a/Simplenote/Classes/UIGestureRecognizer+Simplenote.swift b/Simplenote/Classes/UIGestureRecognizer+Simplenote.swift index dbcd32bd5..d9c0cd853 100644 --- a/Simplenote/Classes/UIGestureRecognizer+Simplenote.swift +++ b/Simplenote/Classes/UIGestureRecognizer+Simplenote.swift @@ -21,8 +21,16 @@ extension UIGestureRecognizer { locationInContainer.x -= textView.textContainerInset.left locationInContainer.y -= textView.textContainerInset.top - return textView.layoutManager.characterIndex(for: locationInContainer, - in: textView.textContainer, - fractionOfDistanceBetweenInsertionPoints: nil) + guard #available(iOS 17.0, *), + let textLayoutManager = textView.textLayoutManager else { + // TextKit 1 fallback + return textView.layoutManager.characterIndex( + for: locationInContainer, + in: textView.textContainer, + fractionOfDistanceBetweenInsertionPoints: nil) + } + + // TextKit 2 + return textLayoutManager.characterIndex(for: locationInContainer) ?? .zero } } diff --git a/Simplenote/NSTextLayoutFragment.swift b/Simplenote/NSTextLayoutFragment.swift new file mode 100644 index 000000000..b9187db1d --- /dev/null +++ b/Simplenote/NSTextLayoutFragment.swift @@ -0,0 +1,17 @@ +// +// NSTextLayoutFragment.swift +// Simplenote +// +// Created by Charlie Scheer on 12/20/24. +// Copyright © 2024 Automattic. All rights reserved. +// + +extension NSTextLayoutFragment { + @available(iOS 17.0, *) + func characterIndex(for location: CGPoint) -> Int? { + guard let lineFragment = textLineFragment(forVerticalOffset: location.y, requiresExactMatch: true) else { + return nil + } + return lineFragment.characterIndex(for: location) + } +} diff --git a/Simplenote/NSTextLayoutManager+Simplenote.swift b/Simplenote/NSTextLayoutManager+Simplenote.swift new file mode 100644 index 000000000..7350b4bf7 --- /dev/null +++ b/Simplenote/NSTextLayoutManager+Simplenote.swift @@ -0,0 +1,17 @@ +// +// NSTextLayoutManager+Simplenote.swift +// Simplenote +// +// Created by Charlie Scheer on 12/20/24. +// Copyright © 2024 Automattic. All rights reserved. +// + +extension NSTextLayoutManager { + @available(iOS 17.0, *) + func characterIndex(for location: CGPoint) -> Int? { + guard let lineFragment = textLayoutFragment(for: location) else { + return nil + } + return lineFragment.characterIndex(for: location) + } +} diff --git a/Simplenote/SPTextView.swift b/Simplenote/SPTextView.swift index a34315b24..38e8cfe83 100644 --- a/Simplenote/SPTextView.swift +++ b/Simplenote/SPTextView.swift @@ -14,7 +14,7 @@ extension SPTextView { container.heightTracksTextView = true - if #available(iOS 16.0, *) { + if #available(iOS 17.0, *) { let textLayoutManager = NSTextLayoutManager() let contentStorage = NSTextContentStorage() contentStorage.delegate = self