Post not yet marked as solved
The crash happens in:
iOS 16 - 96%
iOS 17 - 3%
iPad OS 16 - 1%
Post not yet marked as solved
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?
Post not yet marked as solved
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
Post not yet marked as solved
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?
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.
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.
Post not yet marked as solved
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.
Post not yet marked as solved
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.
Post not yet marked as solved
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
Post not yet marked as solved
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:
Post not yet marked as solved
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?
Post not yet marked as solved
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?
Post not yet marked as solved
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?
Post not yet marked as solved
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?
Post not yet marked as solved
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?
Post not yet marked as solved
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.
Post not yet marked as solved
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?
Post not yet marked as solved
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
Post not yet marked as solved
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
Post not yet marked as solved
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