Manage text storage and perform custom layout of text-based content in your app's views using TextKit.

TextKit Documentation

Posts under TextKit tag

47 Posts
Sort by:
Post not yet marked as solved
2 Replies
446 Views
I am developing a richtext editor using UITextView, and I found a BUG of UITextViewDelegate's method: optional func textView( _ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String ) -> Bool This BUG occurs when user tries to delete a selected text. For example: When user deleting "llo" in "hello" by select "llo" press delete The correct range should be NSRange(2, 3),but the actual range is NSRange(1, 4),and replacementText is a empty string. Which means it wants to replace "ello" with "" and it's not right. The final result of this action is "llo" gets replaced by "", which is correct and corrupted with delegate method's range info! No wonder that we can't find this BUG until we test the delegate method. However, when trying to replace "llo" with some text(NOT DELETING), the range info is correct. In conculsion, the caller of the delegate method compute the range wrong when user try to delete a selected text.
Posted
by laishere.
Last updated
.
Post not yet marked as solved
2 Replies
496 Views
I've run into a very strange bug on macOS Sonoma, and I'm guessing it's caused by TextKit 1/2 compatibility issues. Whenever the caret is drawn on the last line of NSTextView, the anti-aliasing or drawing rect or something changes, causing the text in the current line fragment rect to wiggle up and down. See the example below: (Note that in this image, I'm using custom caret drawing to test that if that would solve something — it didn't.) The effect is more apparent on non-Retina displays, but still present on Retina screens as well. I'm using TextKit 1 because I'm maintaining compatibility with older systems, and can't move away just yet. The behavior can't be reproduced on any other OS than Sonoma. My NSTextView is scaled using scaleUnitSquareToSize but no matter the scale, the issue seems to be present. Text view also has custom input attributes, which includes line height, but drawing doesn't appear to be affected by that. Has anyone else encountered similar issues on Sonoma, and do you have any suggestions on how to proceed with debugging this?
Posted
by tritonus.
Last updated
.
Post not yet marked as solved
2 Replies
1.7k Views
I'm trying to implement custom NSTextContentManager and use it with NSTextView, however it seems that NSTextView expect NSTextContentStorage all the time. final class MyTextContentManager: NSTextContentManager { // ... } It's added to layout manager, and NSTextView instance finds it properly: let textContentManager = MyTextContentManager() textContentManager.addTextLayoutManager(textLayoutManager) however, when I use it, I see errors at: [MyTextContentManager textStorage]: unrecognized selector sent to instance 0x600003d84870 the textStorage property is part of NSTextStorageObserving, that is not NSTextContentManager interface. It looks like NSTextView is not ready to work with custom NSTextContentManager. What did I miss?
Posted Last updated
.
Post not yet marked as solved
0 Replies
389 Views
I have been getting crash reports from users of my Mac app on Sonoma 14.0 and 14.1 when typing into an NSTextView subclass. The crash logs I have show involvement of the spell-checking system - NSTestCheckingController, NSSpellChecker, and NSCorrectionPanel. The crash is because of an exception being thrown. The throwing method is either [NSString getParagraphStart:end:contentsEnd:forRange:] or [NSTextStorage ensureAttributesAreFixedInRange:]. I have not yet reproduced the crash. I have tried modifying the reference finding process to simply link every word, via NSStringEnumerationByWords. The text view in question recognizes certain things in the entered text and adds hyperlinks to the text while the user is typing. It re-parses and re-adds the links on every key press (via overriding the didChangeText method), on a background thread. From user reports, I have learned that: The crash only occurs on macOS 14.0 and 14.1, not on previous versions The call stack always involves the spell checker, and sometimes involves adding recognized links to the text storage (the call to DispatchQueue.main.async in the code below) The crash stops happening if the user turns off the system spell checker in System Settings -> Keyboard -> Edit on an Input Source -> Correct Spelling Automatically switch The crash does not happen when there are no links in the text view. Here is the relevant code: extension NSMutableAttributedString { func batchUpdates(_ updates: () -> ()) { self.beginEditing() updates() self.endEditing() } } class MyTextView : NSTextView { func didChangeText() { super.didChangeText() findReferences() } var parseToken: CancelationToken? = nil let parseQueue = DispatchQueue(label: "com.myapp.ref_parser") private func findReferences() { guard let storage = self.textStorage else { return } self.parseToken?.requestCancel() let token = CancelationToken() self.parseToken = token let text = storage.string self.parseQueue.async { if token.cancelRequested { return } let refs = RefParser.findReferences(inText: text, cancelationToken: token) DispatchQueue.main.async { if !token.cancelRequested { storage.batchUpdates { var linkRanges: [NSRange] = [] storage.enumerateAttribute(.link, in: NSRange(location: 0, length: storage.length)) { linkValue, linkRange, stop in if let linkUrl = linkValue as? NSURL { linkRanges.append(linkRange) } } for rng in linkRanges { storage.removeAttribute(.link, range: rng) } for r in refs { storage.addAttribute(.link, value: r.url, range: r.range) } } self.verseParseToken = nil } } } } } I've filed this as FB13306015 if any engineers see this. Can anyone
Posted Last updated
.
Post not yet marked as solved
1 Replies
897 Views
I am trying to add custom attributes on-the-fly. To make it work, I subclassed NSTextLayoutFragment, and overrode .textLineFragments property to return custom made NSTextLineFragment objects. But if I override it, TextKit2 no longer render the text and selection also doesn't work. It's same even if provide NSTextLineFragment with exactly same properties (attributed string and range). In WWDC 22 video, you told me that NSTextLayoutFragment and NSTextLineFragment are all immutable and have value semantics. But it doesn't work with different object, therefore seems still have very strong reference semantics. How to make it work with custom attributes? P.S. I also reported this as a feedback -- https://feedbackassistant.apple.com/feedback/12443016
Posted
by Eonil.
Last updated
.
Post not yet marked as solved
1 Replies
372 Views
I am interested in overriding the default find/replace dialog which comes up from the performFindPanelAction method. I want to add additional things you can search for like text color and text style/format. How would I go about doing this?
Posted Last updated
.
Post marked as solved
1 Replies
499 Views
This is a tricky one. I have a shipping product which, when compiled under Xcode 14.3.1 works as expected on Sonoma. If the same project is recompiled with Xcode 15, the subclass of NSTextView will not display correctly (text is same color as background). I am also using a custom NSLayoutManager (to draw invisibles). Unfortunately, there is an intermittent aspect to this. I use this subclass in several places and it works on my setup on the main editor, but not with some customers. Then I found a different use of the same subclass that does not work for me. When it does not work, it is consistent for that user. I have manually marked the textViews as using TextKit 1, with no change. I also tried the Clips Bounds to yes, again no change. If I change the class to NSTextView, the text displays properly, but I lose existing functionality. There appears to be some undocumented behavior change in Xcode 15 (or when linking against Sonoma SDK) that for subclasses of NSTextView (stored in XIB files). I know that there is a push to move toward TextKit 2, but it seems TextKit 1 support was possibly changed as well. The text is there and I can edit it, double click, copy and paste it, it is just invisible, when compiled with Xcode 15 (also 15.1). It has to be something very subtle that the subclassed TextView from one XIB will work, but from another XIB will not. Does anyone have any insight into the potential change with TextKit 1 implementation? Thanks.
Posted
by mmunz.
Last updated
.
Post not yet marked as solved
1 Replies
770 Views
I've a custom UIView to render a large piece of text using CATiledLayers. My draw(rect:) implementation is quite simple: override func draw(_ rect: CGRect) { 	let range = layoutManager.glyphRange(forBoundingRect: rect, in: textContainer) 	layoutManager.drawBackground(forGlyphRange: range, at: .zero) 	layoutManager.drawGlyphs(forGlyphRange: range, at: .zero) } This code works without any issue on iOS 13, but fails with a crash on iOS 14 simulator: Thread 9: EXC_BAD_ACCESS (code=1, address=0x28) #0 0x00007fff2396a3f5 in _NSLayoutTreeMoveToGlyphIndex () I can see that draw(rect:) is being called from different threads in both iOS 13 and 14. However the EXC_BAD_ACCESS has never happened so far in iOS 13. I wonder whether this is a behaviour change in iOS 14 or an issue. If this is a change in NSLayoutManager, then is it still possible to use CATiledLayer to render large amount of text in coordination with NSLayoutManager?
Posted
by haldun-gh.
Last updated
.
Post marked as solved
3 Replies
491 Views
After upgrading to Sonoma and Xcode 15, some of the NSTextViews in my app no longer show up. However, if entering Debug View Hierarchy, you can see them just fine. This only seems to happen with certain views that are created programmatically.
Posted
by Dalaplan.
Last updated
.
Post not yet marked as solved
0 Replies
358 Views
This function on NSTextLayoutManager has the following signature func enumerateTextSegments( in textRange: NSTextRange, type: NSTextLayoutManager.SegmentType, options: NSTextLayoutManager.SegmentOptions = [], using block: (NSTextRange?, CGRect, CGFloat, NSTextContainer) -> Bool ) The documentation here doesn't define what the CGRect and CGFloat passed to block are. However, looking at sample code Using TextKit2 To Interact With Text, they seem to be the frame for the textsegment and baselineposition respectively. But, the textSegmentFrame seems to start at origin.x = 5.0 when text is empty. Is this some starting offset for text segments? I don't seem to be able to find mention of this anywhere.
Posted
by georgemp.
Last updated
.
Post not yet marked as solved
1 Replies
376 Views
In this code sample, the TextDocumentView has a variable set to 5.0. Any idea where this 5.0 number is coming from? It is used internally in other calculations within TextDocumentLayer. While it works, I'm not sure what it signifies, and how to adapt it to our projects? Thanks
Posted
by georgemp.
Last updated
.
Post not yet marked as solved
0 Replies
409 Views
In macOS 14, NSAttributedString.Key.verticalGlyphForm has been deprecated. This key was previously used for rendering text vertically, particularly in languages like Chinese, Japanese, and Korean. I'm curious about the reasons behind its deprecation and would appreciate insights into alternative methods for displaying vertical text.
Posted
by oden-oden.
Last updated
.
Post not yet marked as solved
0 Replies
309 Views
Returning false from NSTextContentManagerDelegate.textContentManager(_:shouldEnumerate:options:) produces huge gaps in my layout instead of showing a continuous block of text. Instead of omiting the layout of the hidden element, there is a blank space that shows that appears to have the same size in layout as the omitted text element. Why is this happening and how can I prevent this? Example:
Posted
by eaigner.
Last updated
.
Post not yet marked as solved
0 Replies
352 Views
When using NSTextLayoutManager.addRenderingAttribute(.backgroundColor, value: NSColor.red, for: range), the background color for a line is only drawn as far as the last visible character. There is also a thin space between the lines where the background color is not visible. Whe using NSLayoutManager.addTemporaryAttribute(.backgroundColor, value: NSColor.red, forCharacterRange: range), the background color is drawn also for newline characters and soft line wraps. I would like to achieve the effect of using NSLayoutManager.addTemporaryAttribute(.backgroundColor, value: NSColor.red, forCharacterRange: range), but since I'm targeting TextKit 2, I have to avoid using NSLayoutManager. Is there a way to achieve this with NSTextLayoutManager or one of the other related classes in TextKit 2?
Posted
by Nickkk.
Last updated
.
Post not yet marked as solved
1 Replies
366 Views
If I set an exclusion path, e.g. in TextDocumentViewController.viewDidLoad() like: textLayoutManager.textContainer?.exclusionPaths = [bezierPath] I am getting a "Unexpectedly found nil while unwrapping an Optional value" crash in TextDocumentView.adjustViewportOffset() because viewportRange is nil on textViewportLayoutController. Setting an exclusion path also causes weird behavior for textLayoutManager.enumerateTextLayoutFragments() which refuses to iterate if the .ensuresLayout option is set. The problem happens in both iOS and macOS (though I only care about iOS). I'm able to get exclusionPaths to work fine if I use UITextView, but the performance is unworkable there. I also was able to set exclusionPaths in the STTextView project in GitHub, but I was unable to identify anything specific that code was doing differently. Anyone have ideas as to what else needs to happen to make exclusionPaths work?
Posted
by glshims.
Last updated
.
Post not yet marked as solved
1 Replies
454 Views
I'm working on an app displaying a few hundred custom labels. The custom label is modelled after UILabel. When I implement the custom label using TextKit, the app is using about 0.5 GB of memory. When I implement it using TextKit2, it takes about 1.2 GB. Did anyone notice such a big difference in memory usage between TextKit and TextKit2? For how long will TextKit be around before being deprecated?
Posted Last updated
.
Post not yet marked as solved
4 Replies
2.8k Views
In the "old" TextKit, page-based layout is accomplished by providing an array of NSTextContainers to NSLayoutManager, each with its own NSTextView. TextKit 2, NSTextLayoutManager allows only a single text container. Additionally, NSTextParagraph seems to be the only concrete NSTextElement class. Paragraphs often need to break across page boundaries. How would one implement page-based layout in TextKit 2?
Posted
by sjs.
Last updated
.
Post not yet marked as solved
0 Replies
478 Views
I am working on supporting some formatted text editing in my app, and I've been experimenting with copy and paste support for formatted text. I discovered that NSAttributedString implements NSItemProviderWriting, which means I can give it to UIPasteboard via setObjects and all the built-in attributes transfer perfectly if I then paste it into another text view, or even another app that behaves itself. But if I have custom attributes in my attributed string, having their values implement Codable doesn't let them transfer across the clipboard. In my implementation of textPasteConfigurationSupporting(_: transform:), I try to get an attributed string like this: let attr = item.itemProvider.loadObject(ofClass: NSAttributedString.self) { val, err in //...handle here } I get an error like this: Error Domain=NSItemProviderErrorDomain Code=-1000 "Cannot load representation of type com.apple.uikit.attributedstring" UserInfo={NSLocalizedDescription=Cannot load representation of type com.apple.uikit.attributedstring, NSUnderlyingError=0x600003e7bea0 {Error Domain=NSCocoaErrorDomain Code=260 "The file “b036c42113e34c2f9d9af14d6fefcbd534f627d6” couldn’t be opened because there is no such file." UserInfo={NSURL=file:///Users/username/Library/Developer/CoreSimulator/Devices/86E8BDD4-B6AA-4170-B0EB-57C74EC7DDF0/data/Library/Caches/com.apple.Pasteboard/eb77e5f8f043896faf63b5041f0fbd121db984dd/b036c42113e34c2f9d9af14d6fefcbd534f627d6, NSFilePath=/Users/username/Library/Developer/CoreSimulator/Devices/86E8BDD4-B6AA-4170-B0EB-57C74EC7DDF0/data/Library/Caches/com.apple.Pasteboard/eb77e5f8f043896faf63b5041f0fbd121db984dd/b036c42113e34c2f9d9af14d6fefcbd534f627d6, NSUnderlyingError=0x600003e7ac70 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}}} But I tried making my custom attribute values implement NSSecureCoding, and then it worked. Why is Codable conformance not enough here? Is it because the code that serializes and deserializes is still in Objective-C and isn't aware of Codable? Will this change as the open-source Foundation in Swift work continues?
Posted Last updated
.
Post not yet marked as solved
0 Replies
488 Views
Hey, Maybe someone can explain me why NSLayoutManager is giving me "unstable" layout? It's like it decides to include line-height-multiple of 1.2 sometimes: We're doing some custom text rendering and if I change font size by a bit (or other property), then suddenly there is this extra text spacing. What we noticed so far is that there is something special about the first font that was set on NSTextStorage, in that case text has no extra spacing. Once you change it to slightly modified font else - the extra spacing is introduced. It's not NSParagraphStyle.lineHeightMultiple - if I set it to something non default then it acts as extra, but doesn't solve the "layout instability" issue. Any clue what's causing this? or how to make it to be stable?
Posted
by _Paulius_.
Last updated
.