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

Posts under TextKit tag

176 Posts

Post

Replies

Boosts

Views

Activity

Why was NSAttributedString.Key.verticalGlyphForm Deprecated in macOS 14, and How to Handle Vertical Text Now?
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.
2
1
793
Oct ’23
Text background color for newlines in TextKit 2
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?
0
1
592
Sep ’23
Unable to add exclusionPaths to "Meet TextKit 2" sample code
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?
1
0
622
Sep ’23
TextKit2 vs TextKit1 memory usage
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?
1
0
794
Aug ’23
Page-based layouts with TextKit 2?
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?
4
0
3.6k
Aug ’23
Unstable text layout in NSLayoutManager
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?
0
0
702
Jul ’23
Doing UITextView layout in advance and in the background
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?
1
0
898
Jul ’23
Vertical text from left to right with NSTextView
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.
0
0
878
Jul ’23
When does viewport bounds gets recalculated?
The demo app has a problem: if you vertically shrink the window using the bottom edge and move it to position y0, then drag the bottom edge again to another position y1, parts of the document after y0 will not be rendered due to the fact the viewport bounds are not recalculated when frame height changes. My fix to this problem is to remove the check that inspects whether the text container width changed and always set the value of the text container size used by the text layout manager whenever frame size changes: this does fix the problem mentioned above. However, I would like to know exactly when will the text layout manager recalculate the viewport bounds. This is not documented anywhere, nor in the video.
1
0
1.2k
Jun ’23
Ending TextList Madness
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
0
0
722
Jun ’23
Exclusion path splits word at the end of the line independent of lineBreakMode
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
0
1
1.1k
Jun ’23
Why does NSLayoutManager's text height/lines calculation differ from UILabel's behavior with different line break modes?
We are curious about what caused the mismatch between UILabel rendering and NSLayoutManager calculation with different lineBreakMode. Can we say that NSLayoutManager doesn't support lineBreakMode except .byWordWrapping? First, we have a function that creates an attributed string. In this function, we assign .byTruncatingTail to the paragraphStyle lineBreakMode. func createAttributedString(with text: String) -> NSMutableAttributedString { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineBreakMode = .byTruncatingTail let attributedString = NSMutableAttributedString(string: text, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 35), NSAttributedString.Key.paragraphStyle: paragraphStyle]) return attributedString } If we create a label with the following settings: let text = (1..<20).reduce("", { "\($0)" + "\($1)-"}) let attributedString = createAttributedString(with: text) let label = UILabel(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 500))) label.numberOfLines = 0 label.lineBreakMode = .byTruncatingTail label.attributedText = attributedString we get the result: If we use the same attributed string maker function and use NSLayoutManager to calculate the height for a certain width with the following code reference from Apple's documentation: let text = (1..<20).reduce("", { "\($0)" + "\($1)-"}) let attributedString = createAttributedString(with: text) let textContainer = NSTextContainer(size: CGSize(width: 100, height: CGFloat.greatestFiniteMagnitude)) let layoutManager = NSLayoutManager() let textStorage = NSTextStorage(attributedString: attributedString) layoutManager.addTextContainer(textContainer) textStorage.addLayoutManager(layoutManager) textContainer.lineFragmentPadding = 0.0 textContainer.maximumNumberOfLines = 0 textContainer.lineBreakMode = .byTruncatingTail layoutManager.ensureLayout(for: textContainer) layoutManager.glyphRange(for: textContainer) let textFrame = layoutManager.usedRect(for: textContainer) print("textSize: \(textFrame.size)") we get the print result: If we assign the calculated rect to the label, we get: This result does not match the initial label we created. If we change the lineBreakMode in the attributed string maker to the default value, .byWordWrapping, we can get the result for multiple lines, which has the same height as the initial label. If we assign the calculated rect to the label, we get: We are curious about what caused the mismatch between UILabel rendering and NSLayoutManager calculation with different lineBreakMode. Can we say that NSLayoutManager doesn't support lineBreakMode except .byWordWrapping? We are also curious about the design thought behind having the default lineBreakMode for UILabel be .byTruncatingTail and for NSMutableParagraphStyle be .byWordWrapping.
0
0
1.5k
May ’23
How can I automatically insert the next NSTextList list item on return in iOS 16?
I'm adding some text formatting support to an editable text view in my app, and I want to include bulleted lists. I specifically want to have the standard behavior where if you're typing a line of text in a list and press Enter, the next bullet item is automatically inserted. This appears to somewhat work out-of-the-box with NSTextList on iOS 16, unless the line you're editing is at the very end of the document. This is apparent in the sample code for WWDC 2022 session 10090 (project is here): Run that sample app and switch to the List tab Put your cursor at the end of any line except the last one and press enter. You get a new list item, as expected. Put your cursor at the end of the very last line and press Enter. You don't get a list item until you type something on that line. I've likewise found that if I have this text in a text view: Hello World\n\nList here\n If I format the text List here as a bulleted list (by creating an NSMutableParagraphStyle with its textLists property set and adding it to the attributed string for that range) and press Enter at the end of the word Here, I get a new bullet item automatically. But if I do the same thing without having that last newline after Here, the new list item is not inserted. How can I make the list auto-continuation behavior work at the end of the document?
1
0
1k
Apr ’23
TextKit 2 - How to render multiple fragments in one NSTextParagraph?
Hello TextKit 2 development team, I am trying to render a paragraph of text with different styles for specific words. I have carefully reviewed the sample code provided in the documentation but I couldn't find any method to display NSTextLayoutFragment with different styles in a single NSTextParagraph. Currently, I am using NSTextContentStorageDelegate.textContentStorage(:textParagraphWith:) to output the corresponding NSTextParagraph, and then using NSTextLayoutManagerDelegate.textLayoutManager(:textLayoutFragmentFor:in:) to output the corresponding TextLayoutFragment. However, this only allows me to customize the style on a paragraph, while what I need is to customize the style of certain words within the paragraph. I have guessed that I can achieve this by specifying NSTextLineFragment properties, but in reality, it does not display any content. Can you please guide me on how to accomplish this task?
1
2
855
Mar ’23
App crashes with error "Fatal Exception: NSInvalidArgumentException -[<NSTextContentStorage: 0x281dc68a0> locationFromLocation:withOffset:] received invalid location (null)"
From IOS 16 onwards my app which uses UITextView in iOS for editing, started crashing for a number of users with error. App crashes with error "Fatal Exception: NSInvalidArgumentException -[<NSTextContentStorage: 0x281dc68a0> locationFromLocation:withOffset:] received invalid location (null)" After researching we found that this could be an issue with the new textkit engine of UITextView. And to restore the previous behavior and avoid the crash, we explicitly set the UITextView to use TextKit 1 and re-released the app with Xcode 14.0. But unfortunately, even after this change, this crash is not gone and users are still facing this issue. Adding a link that suggests a response from the Apple developers team to change textkit engine from textkit 2 to textkit 1 for this issue. https://lapcatsoftware.com/articles/textview5.html I'm also adding complete stack trace of this crash Fatal Exception: NSInvalidArgumentException 0 CoreFoundation 0x9e88 __exceptionPreprocess 1 libobjc.A.dylib 0x178d8 objc_exception_throw 2 CoreFoundation 0xf9c58 __CFDictionaryCreateGeneric 3 UIFoundation 0x220e0 -[NSTextContentStorage locationFromLocation:withOffset:] 4 UIFoundation 0x9f104 -[NSTextLineFragment textLineFragmentRange] 5 UIFoundation 0x86e70 __95-[NSTextLayoutManager _copyTextLineFragmentRangeForPoint:inContainerAtLocation:pointPlacement:]_block_invoke 6 CoreFoundation 0xa3a0 NSARRAY_IS_CALLING_OUT_TO_A_BLOCK 7 CoreFoundation 0x910e0 -[__NSArrayI enumerateObjectsWithOptions:usingBlock:] 8 UIFoundation 0x86cfc -[NSTextLayoutManager _copyTextLineFragmentRangeForPoint:inContainerAtLocation:pointPlacement:] 9 UIFoundation 0x86eb4 -[NSTextLayoutManager lineFragmentRangeForPoint:inContainerAtLocation:] 10 UIFoundation 0x7e6ec -[NSTextSelectionNavigation _lineFragmentInfoForPoint:inContainerAtLocation:bounds:layoutOrientation:beforeLineFragment:afterLineFragment:lineFragmentRange:] 11 UIFoundation 0x815d8 -[NSTextSelectionNavigation textSelectionsInteractingAtPoint:inContainerAtLocation:anchors:modifiers:selecting:bounds:] 12 UIKitCore 0x98f5d8 -[_UITextKit2LayoutController cursorPositionAtPoint:inContainer:] 13 UIKitCore 0xf33d88 -[UITextInputController _characterPositionForPoint:] 14 UIKitCore 0xf4a2cc -[UITextView closestPositionToPoint:] 15 UIKitCore 0xedef5c -[UITextSelectionInteraction tappedToPositionCursorWithGesture:atPoint:granularity:completionHandler:] 16 UIKitCore 0xedecc8 -[UITextSelectionInteraction _checkForRepeatedTap:gestureLocationOut:] 17 UIKitCore 0xedf524 -[UITextSelectionInteraction _handleMultiTapGesture:] 18 UIKitCore 0x24ed54 -[UIApplication sendAction:to:from:forEvent:] 19 UIKitCore 0x9448fc -[UITextMultiTapRecognizer onStateUpdate:] 20 UIKitCore 0xbe874 -[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] 21 UIKitCore 0x42c9f0 _UIGestureRecognizerSendTargetActions 22 UIKitCore 0x1a60a4 _UIGestureRecognizerSendActions 23 UIKitCore 0x86a14 -[UIGestureRecognizer _updateGestureForActiveEvents] 24 UIKitCore 0x1328d8 _UIGestureEnvironmentUpdate 25 UIKitCore 0x9b7e14 -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] 26 UIKitCore 0xf731c -[UIGestureEnvironment _updateForEvent:window:] 27 UIKitCore 0xfbc74 -[UIWindow sendEvent:] 28 UIKitCore 0xfaf44 -[UIApplication sendEvent:] 29 Aspen 0x6963c0 TimerApplication.sendEvent(_:) () 30 Aspen 0x696834 @objc TimerApplication.sendEvent(_:) () 31 UIKitCore 0xfa600 __dispatchPreprocessedEventFromEventQueue Please let us know how can we fix this issue, app was working fine sine IOS 11.0 but suddenly it has started crashing. Currently users are also facing this issue in updated IOS version IOS 16.1.1 Device - iPhone 12 , iPhone 12 Pro, iPhone 13
16
6
6k
Mar ’23
SwiftUI Text Editor Crash with Apple Pencil Long Press
Using Apple Pencil with SwiftUI text editor, when the user presses and holds in the middle of text already in the editor box a crash occurs. This is very reproducible even on this minimal example. import SwiftUI struct ContentView: View { @State private var text: String = "The quick brown fox jumps over the lazy dog" var body: some View { VStack { TextEditor(text: $text) .background(Color.init(red: 242, green: 242, blue: 242)) .border(.black) .frame(height: 200) } .padding() } } When tapping and holding somewhere in the middle of the sentence, for example on the word "jumps" the app crashes with an exception reading: Thread 1: "NSTextContentStorage: Inconsistent element cache state. Elements for range {0, 43} are already cached while trying to insert" The stack trace is: I have attempted to use the approach discussed in this post to make the underlying UITextView use TextKit 1, but could find no UITextView in the underlying UIView hierarchy. Any advice would be greatly appreciated.
2
3
1.9k
Feb ’23
iOS 16.1 breaks UITextView tokenizer for words (rangeEnclosingPosition: withGranularity : inDirection:)
We have an messaging application. In which we have a feature where it detect phone number and on tap of it we provide other options to user. Until release of iOS 16.1 version, it was working perfectly fine. But, After realise of iOS 16.1 and above we are experiencing a strange issue where it is not detecting the tap and not showing other options.  After debugging the code, we found that we are using below UIKit function to get the range or number on tap. But, In iOS 16.1 version it is returning null value. whereas, it was returning the range of number where it was tapped.  Below is the function which is returning nil value, Also attaching screenshot of code for better understanding.   **  textRange = [tokenizer rangeEnclosingPosition:tapPosition                           withGranularity:UITextGranularityWord                             inDirection:UITextLayoutDirectionRight];** The built-in UITextView tokenizer function rangeEnclosingPosition(_:with:inDirection:) seems to have broken in iOS 16.1 for the “word” granularity. Word: doesn't seem to ever return a range. We also tried this in a sample project. But, these issue is same on that project to. Please fix this issue on upcoming release. As, this being one of the crucial part of our health messaging app.  If possible, Please provide us a work around for this.
2
0
1.9k
Jan ’23
NSTextView invisible in app, visible in Debug View Hierarchy
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.
Replies
3
Boosts
0
Views
1.1k
Activity
Oct ’23
Padding Variable in code sample `Using TextKit2 To Interact With Text`
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
Replies
2
Boosts
0
Views
738
Activity
Oct ’23
Why was NSAttributedString.Key.verticalGlyphForm Deprecated in macOS 14, and How to Handle Vertical Text Now?
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.
Replies
2
Boosts
1
Views
793
Activity
Oct ’23
Text background color for newlines in TextKit 2
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?
Replies
0
Boosts
1
Views
592
Activity
Sep ’23
Unable to add exclusionPaths to "Meet TextKit 2" sample code
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?
Replies
1
Boosts
0
Views
622
Activity
Sep ’23
TextKit2 vs TextKit1 memory usage
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?
Replies
1
Boosts
0
Views
794
Activity
Aug ’23
Page-based layouts with TextKit 2?
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?
Replies
4
Boosts
0
Views
3.6k
Activity
Aug ’23
Unstable text layout in NSLayoutManager
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?
Replies
0
Boosts
0
Views
702
Activity
Jul ’23
Doing UITextView layout in advance and in the background
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?
Replies
1
Boosts
0
Views
898
Activity
Jul ’23
Vertical text from left to right with NSTextView
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.
Replies
0
Boosts
0
Views
878
Activity
Jul ’23
When does viewport bounds gets recalculated?
The demo app has a problem: if you vertically shrink the window using the bottom edge and move it to position y0, then drag the bottom edge again to another position y1, parts of the document after y0 will not be rendered due to the fact the viewport bounds are not recalculated when frame height changes. My fix to this problem is to remove the check that inspects whether the text container width changed and always set the value of the text container size used by the text layout manager whenever frame size changes: this does fix the problem mentioned above. However, I would like to know exactly when will the text layout manager recalculate the viewport bounds. This is not documented anywhere, nor in the video.
Replies
1
Boosts
0
Views
1.2k
Activity
Jun ’23
Ending TextList Madness
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
Replies
0
Boosts
0
Views
722
Activity
Jun ’23
Exclusion path splits word at the end of the line independent of lineBreakMode
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
Replies
0
Boosts
1
Views
1.1k
Activity
Jun ’23
Why does NSLayoutManager's text height/lines calculation differ from UILabel's behavior with different line break modes?
We are curious about what caused the mismatch between UILabel rendering and NSLayoutManager calculation with different lineBreakMode. Can we say that NSLayoutManager doesn't support lineBreakMode except .byWordWrapping? First, we have a function that creates an attributed string. In this function, we assign .byTruncatingTail to the paragraphStyle lineBreakMode. func createAttributedString(with text: String) -> NSMutableAttributedString { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineBreakMode = .byTruncatingTail let attributedString = NSMutableAttributedString(string: text, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 35), NSAttributedString.Key.paragraphStyle: paragraphStyle]) return attributedString } If we create a label with the following settings: let text = (1..<20).reduce("", { "\($0)" + "\($1)-"}) let attributedString = createAttributedString(with: text) let label = UILabel(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 500))) label.numberOfLines = 0 label.lineBreakMode = .byTruncatingTail label.attributedText = attributedString we get the result: If we use the same attributed string maker function and use NSLayoutManager to calculate the height for a certain width with the following code reference from Apple's documentation: let text = (1..<20).reduce("", { "\($0)" + "\($1)-"}) let attributedString = createAttributedString(with: text) let textContainer = NSTextContainer(size: CGSize(width: 100, height: CGFloat.greatestFiniteMagnitude)) let layoutManager = NSLayoutManager() let textStorage = NSTextStorage(attributedString: attributedString) layoutManager.addTextContainer(textContainer) textStorage.addLayoutManager(layoutManager) textContainer.lineFragmentPadding = 0.0 textContainer.maximumNumberOfLines = 0 textContainer.lineBreakMode = .byTruncatingTail layoutManager.ensureLayout(for: textContainer) layoutManager.glyphRange(for: textContainer) let textFrame = layoutManager.usedRect(for: textContainer) print("textSize: \(textFrame.size)") we get the print result: If we assign the calculated rect to the label, we get: This result does not match the initial label we created. If we change the lineBreakMode in the attributed string maker to the default value, .byWordWrapping, we can get the result for multiple lines, which has the same height as the initial label. If we assign the calculated rect to the label, we get: We are curious about what caused the mismatch between UILabel rendering and NSLayoutManager calculation with different lineBreakMode. Can we say that NSLayoutManager doesn't support lineBreakMode except .byWordWrapping? We are also curious about the design thought behind having the default lineBreakMode for UILabel be .byTruncatingTail and for NSMutableParagraphStyle be .byWordWrapping.
Replies
0
Boosts
0
Views
1.5k
Activity
May ’23
How can I automatically insert the next NSTextList list item on return in iOS 16?
I'm adding some text formatting support to an editable text view in my app, and I want to include bulleted lists. I specifically want to have the standard behavior where if you're typing a line of text in a list and press Enter, the next bullet item is automatically inserted. This appears to somewhat work out-of-the-box with NSTextList on iOS 16, unless the line you're editing is at the very end of the document. This is apparent in the sample code for WWDC 2022 session 10090 (project is here): Run that sample app and switch to the List tab Put your cursor at the end of any line except the last one and press enter. You get a new list item, as expected. Put your cursor at the end of the very last line and press Enter. You don't get a list item until you type something on that line. I've likewise found that if I have this text in a text view: Hello World\n\nList here\n If I format the text List here as a bulleted list (by creating an NSMutableParagraphStyle with its textLists property set and adding it to the attributed string for that range) and press Enter at the end of the word Here, I get a new bullet item automatically. But if I do the same thing without having that last newline after Here, the new list item is not inserted. How can I make the list auto-continuation behavior work at the end of the document?
Replies
1
Boosts
0
Views
1k
Activity
Apr ’23
UIButton appearance font not working for UIButtonLabel
I set a default UIButton Font. UIButton.appearance().titleLabel?.font = UIFont.systemFont(ofSize: 17) But It not working for UIButton Style Default. It's titleLabel is not UILabel , It's a private class UIButtonLabel. How can I do
Replies
1
Boosts
0
Views
1.4k
Activity
Mar ’23
TextKit 2 - How to render multiple fragments in one NSTextParagraph?
Hello TextKit 2 development team, I am trying to render a paragraph of text with different styles for specific words. I have carefully reviewed the sample code provided in the documentation but I couldn't find any method to display NSTextLayoutFragment with different styles in a single NSTextParagraph. Currently, I am using NSTextContentStorageDelegate.textContentStorage(:textParagraphWith:) to output the corresponding NSTextParagraph, and then using NSTextLayoutManagerDelegate.textLayoutManager(:textLayoutFragmentFor:in:) to output the corresponding TextLayoutFragment. However, this only allows me to customize the style on a paragraph, while what I need is to customize the style of certain words within the paragraph. I have guessed that I can achieve this by specifying NSTextLineFragment properties, but in reality, it does not display any content. Can you please guide me on how to accomplish this task?
Replies
1
Boosts
2
Views
855
Activity
Mar ’23
App crashes with error "Fatal Exception: NSInvalidArgumentException -[<NSTextContentStorage: 0x281dc68a0> locationFromLocation:withOffset:] received invalid location (null)"
From IOS 16 onwards my app which uses UITextView in iOS for editing, started crashing for a number of users with error. App crashes with error "Fatal Exception: NSInvalidArgumentException -[<NSTextContentStorage: 0x281dc68a0> locationFromLocation:withOffset:] received invalid location (null)" After researching we found that this could be an issue with the new textkit engine of UITextView. And to restore the previous behavior and avoid the crash, we explicitly set the UITextView to use TextKit 1 and re-released the app with Xcode 14.0. But unfortunately, even after this change, this crash is not gone and users are still facing this issue. Adding a link that suggests a response from the Apple developers team to change textkit engine from textkit 2 to textkit 1 for this issue. https://lapcatsoftware.com/articles/textview5.html I'm also adding complete stack trace of this crash Fatal Exception: NSInvalidArgumentException 0 CoreFoundation 0x9e88 __exceptionPreprocess 1 libobjc.A.dylib 0x178d8 objc_exception_throw 2 CoreFoundation 0xf9c58 __CFDictionaryCreateGeneric 3 UIFoundation 0x220e0 -[NSTextContentStorage locationFromLocation:withOffset:] 4 UIFoundation 0x9f104 -[NSTextLineFragment textLineFragmentRange] 5 UIFoundation 0x86e70 __95-[NSTextLayoutManager _copyTextLineFragmentRangeForPoint:inContainerAtLocation:pointPlacement:]_block_invoke 6 CoreFoundation 0xa3a0 NSARRAY_IS_CALLING_OUT_TO_A_BLOCK 7 CoreFoundation 0x910e0 -[__NSArrayI enumerateObjectsWithOptions:usingBlock:] 8 UIFoundation 0x86cfc -[NSTextLayoutManager _copyTextLineFragmentRangeForPoint:inContainerAtLocation:pointPlacement:] 9 UIFoundation 0x86eb4 -[NSTextLayoutManager lineFragmentRangeForPoint:inContainerAtLocation:] 10 UIFoundation 0x7e6ec -[NSTextSelectionNavigation _lineFragmentInfoForPoint:inContainerAtLocation:bounds:layoutOrientation:beforeLineFragment:afterLineFragment:lineFragmentRange:] 11 UIFoundation 0x815d8 -[NSTextSelectionNavigation textSelectionsInteractingAtPoint:inContainerAtLocation:anchors:modifiers:selecting:bounds:] 12 UIKitCore 0x98f5d8 -[_UITextKit2LayoutController cursorPositionAtPoint:inContainer:] 13 UIKitCore 0xf33d88 -[UITextInputController _characterPositionForPoint:] 14 UIKitCore 0xf4a2cc -[UITextView closestPositionToPoint:] 15 UIKitCore 0xedef5c -[UITextSelectionInteraction tappedToPositionCursorWithGesture:atPoint:granularity:completionHandler:] 16 UIKitCore 0xedecc8 -[UITextSelectionInteraction _checkForRepeatedTap:gestureLocationOut:] 17 UIKitCore 0xedf524 -[UITextSelectionInteraction _handleMultiTapGesture:] 18 UIKitCore 0x24ed54 -[UIApplication sendAction:to:from:forEvent:] 19 UIKitCore 0x9448fc -[UITextMultiTapRecognizer onStateUpdate:] 20 UIKitCore 0xbe874 -[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] 21 UIKitCore 0x42c9f0 _UIGestureRecognizerSendTargetActions 22 UIKitCore 0x1a60a4 _UIGestureRecognizerSendActions 23 UIKitCore 0x86a14 -[UIGestureRecognizer _updateGestureForActiveEvents] 24 UIKitCore 0x1328d8 _UIGestureEnvironmentUpdate 25 UIKitCore 0x9b7e14 -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] 26 UIKitCore 0xf731c -[UIGestureEnvironment _updateForEvent:window:] 27 UIKitCore 0xfbc74 -[UIWindow sendEvent:] 28 UIKitCore 0xfaf44 -[UIApplication sendEvent:] 29 Aspen 0x6963c0 TimerApplication.sendEvent(_:) () 30 Aspen 0x696834 @objc TimerApplication.sendEvent(_:) () 31 UIKitCore 0xfa600 __dispatchPreprocessedEventFromEventQueue Please let us know how can we fix this issue, app was working fine sine IOS 11.0 but suddenly it has started crashing. Currently users are also facing this issue in updated IOS version IOS 16.1.1 Device - iPhone 12 , iPhone 12 Pro, iPhone 13
Replies
16
Boosts
6
Views
6k
Activity
Mar ’23
SwiftUI Text Editor Crash with Apple Pencil Long Press
Using Apple Pencil with SwiftUI text editor, when the user presses and holds in the middle of text already in the editor box a crash occurs. This is very reproducible even on this minimal example. import SwiftUI struct ContentView: View { @State private var text: String = "The quick brown fox jumps over the lazy dog" var body: some View { VStack { TextEditor(text: $text) .background(Color.init(red: 242, green: 242, blue: 242)) .border(.black) .frame(height: 200) } .padding() } } When tapping and holding somewhere in the middle of the sentence, for example on the word "jumps" the app crashes with an exception reading: Thread 1: "NSTextContentStorage: Inconsistent element cache state. Elements for range {0, 43} are already cached while trying to insert" The stack trace is: I have attempted to use the approach discussed in this post to make the underlying UITextView use TextKit 1, but could find no UITextView in the underlying UIView hierarchy. Any advice would be greatly appreciated.
Replies
2
Boosts
3
Views
1.9k
Activity
Feb ’23
iOS 16.1 breaks UITextView tokenizer for words (rangeEnclosingPosition: withGranularity : inDirection:)
We have an messaging application. In which we have a feature where it detect phone number and on tap of it we provide other options to user. Until release of iOS 16.1 version, it was working perfectly fine. But, After realise of iOS 16.1 and above we are experiencing a strange issue where it is not detecting the tap and not showing other options.  After debugging the code, we found that we are using below UIKit function to get the range or number on tap. But, In iOS 16.1 version it is returning null value. whereas, it was returning the range of number where it was tapped.  Below is the function which is returning nil value, Also attaching screenshot of code for better understanding.   **  textRange = [tokenizer rangeEnclosingPosition:tapPosition                           withGranularity:UITextGranularityWord                             inDirection:UITextLayoutDirectionRight];** The built-in UITextView tokenizer function rangeEnclosingPosition(_:with:inDirection:) seems to have broken in iOS 16.1 for the “word” granularity. Word: doesn't seem to ever return a range. We also tried this in a sample project. But, these issue is same on that project to. Please fix this issue on upcoming release. As, this being one of the crucial part of our health messaging app.  If possible, Please provide us a work around for this.
Replies
2
Boosts
0
Views
1.9k
Activity
Jan ’23