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
526 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
Post not yet marked as solved
0 Replies
411 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
by
Post not yet marked as solved
1 Replies
391 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
by
Post marked as solved
1 Replies
528 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
Post not yet marked as solved
0 Replies
375 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
Post not yet marked as solved
0 Replies
437 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
Post not yet marked as solved
0 Replies
329 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
Post not yet marked as solved
0 Replies
371 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
Post not yet marked as solved
1 Replies
378 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
Post not yet marked as solved
1 Replies
474 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
by
Post not yet marked as solved
0 Replies
501 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
by
Post not yet marked as solved
0 Replies
512 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
Post not yet marked as solved
0 Replies
628 Views
In the WebVTT video subtitle format, subtitles can be horizontal, vertical growing left, or vertical growing right. The natural text direction of NSTextView is horizontal; with setLayoutOrientation(_:) I get vertical text growing left. How can I get vertical text growing right? The documentation says that with setLayoutOrientation(_:) the text view's bounds are rotated by 90° clockwise, but manually rotating them by -90° with boundsRotation = -90 just rotates everything, including the text which should have the same orientation as before, just expanding in the opposite direction.
Posted
by
Post not yet marked as solved
1 Replies
590 Views
Short version: Is it possible to do the expensive parts of setting UITextView attributedText on a background queue in advance of the UITextView main thread setup? Long version: Setting attributedText on UITextView is expensive, and UILabel is not an option for us. Is there a way to create a text container and layout manager ourselves in order to perform the expensive work on another queue in advance of setting up the UITextView? Ideally we wouldn’t need to have the layout manager draw a bitmap that we show instead of the UITextView altogether. We’re currently calling the layout manager’s ensureLayout(..) function on a background queue but not seeing an improvement in the main thread performance of setting the attributedText on the UITextView. Does ensureLayout(..) not compute and cache the layout work? Or is the layout work not the expensive part of setting attributed text?
Posted
by
Post not yet marked as solved
1 Replies
916 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
Post not yet marked as solved
0 Replies
445 Views
I am just playing with NSTextList by creating a sample iOS app. The following is my code. import UIKit class ViewController: UIViewController { lazy var textView: UITextView = { let textView = UITextView() textView.text = "" textView.contentInsetAdjustmentBehavior = .automatic textView.backgroundColor = .white textView.font = UIFont.systemFont(ofSize: 20.0) textView.textColor = .black textView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ textView.widthAnchor.constraint(equalToConstant: 600.0), textView.heightAnchor.constraint(equalToConstant: 600.0) ]) return textView }() lazy var button: UIButton = { let button = UIButton() button.setTitle("End list", for: .normal) button.setTitleColor(.white, for: .normal) button.setTitleColor(.lightGray, for: .highlighted) button.backgroundColor = .black button.layer.cornerRadius = 8.0 button.addTarget(self, action: #selector(fixTapped), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ button.widthAnchor.constraint(equalToConstant: 100.0), button.heightAnchor.constraint(equalToConstant: 42.0) ]) return button }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBlue view.addSubview(textView) view.addSubview(button) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) view.addGestureRecognizer(tapGesture) NSLayoutConstraint.activate([ textView.centerXAnchor.constraint(equalTo: view.centerXAnchor), textView.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20.0) ]) let list = NSTextList(markerFormat: .diamond, options: 0) list.startingItemNumber = 1 let paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle paragraphStyle.textLists = [list] let attributes = [NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 24.0)] let attributedStr = NSMutableAttributedString(string: "\n\n\n\n\n", attributes: attributes) textView.textStorage.setAttributedString(attributedStr) } @objc func fixTapped() { } @objc func dismissKeyboard() { view.endEditing(true) } } When the app launches itself, I get 5 lines of diamond guys as shown in the following screenshot. If I keep pressing the delete key with a connected keyboard, the list will be gone as shown below. But if I press the RETURN key several times, the diamond list will come back as shown below. So how can I end this June TextList madness? In code, I have the dismissKeyboard function if I can end this madness programmatically. Thanks, Señor Tomato Spaghetti Chief Janitor at Southeastern Tomato Spaghetti Trade Association
Posted
by
Post not yet marked as solved
0 Replies
855 Views
When using a UITextView and setting its textContainer's exclusionPaths to a path which lies at the and of a line, the words at the end of the line are split into characters even though setting lineBreakMode to .byWordWrapping. This also happens when moving the image to the end of the line in Apple's sample code from the WWDC session "What's new in TextKit and text views from WWDC22": https://developer.apple.com/documentation/uikit/textkit/enriching_your_text_in_text_views
Posted
by