Meet TextKit 2

RSS for tag

Discuss the WWDC21 session Meet TextKit 2.

View Session

Posts under wwdc21-10061 tag

23 Posts
Sort by:
Post marked as solved
1 Replies
381 Views
Hello :), I want to have a UIView inside of a UITextView. For that I use the new NSTextAttachmentViewProvider class introduced in iOS 15. The views width should always be equal to the width of the UITextView this width should update when, for example, the screen rotates. To do that I am using the tracksTextAttachmentViewBounds property inside of a NSTextAttachmentViewProvider subclass. If I understand correctly, if this property is set to true, the function attachmentBounds(for:location:textContainer:proposedLineFragment:position:) of my NSTextAttachmentViewProvider subclass should be used to determine the views bounds. In the code example below I have set it up in that manner, sadly the function is never called. (The storyboard consists of a UIViewController with a UITextView which four constrains (trailing, leading, bottom, top) are set equal to the safe area, nothing special going on). I have also tried to use a NSTextAttachment subclass in which I override the attachmentBounds(for:location:textContainer:proposedLineFragment:position:) function. It is also not called. The view is appearing, but not with the width and height that I have set in the function (see screenshot below), maybe it is using some default values. When I start typing, the view disappears. I don't know what I am doing wrong. Could somebody help me with that problem? import UIKit class SomeNSTextAttachmentViewProvider : NSTextAttachmentViewProvider { override func loadView() { super.loadView() tracksTextAttachmentViewBounds = true view = UIView() view!.backgroundColor = .purple } override func attachmentBounds( for attributes: [NSAttributedString.Key : Any], location: NSTextLocation, textContainer: NSTextContainer?, proposedLineFragment: CGRect, position: CGPoint ) -> CGRect { return CGRect(x: 0, y: 0, width: proposedLineFragment.width, height: 200) } } class ViewController: UIViewController { @IBOutlet var textView: UITextView? override func viewDidLoad() { super.viewDidLoad() NSTextAttachment.registerViewProviderClass(SomeNSTextAttachmentViewProvider.self, forFileType: "public.data") let mutableAttributedString = NSMutableAttributedString() mutableAttributedString.append(NSAttributedString("purple box: ")) mutableAttributedString.append( NSAttributedString( attachment: NSTextAttachment(data: nil, ofType: "public.data") ) ) textView?.attributedText = mutableAttributedString textView?.font = UIFont.preferredFont(forTextStyle: .body) } }
Posted Last updated
.
Post not yet marked as solved
0 Replies
145 Views
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.
Posted Last updated
.
Post not yet marked as solved
0 Replies
192 Views
During the presentation (19:43) it says the NSTextLayoutFragment 'Layout information for one or more elements'. But from the documentation it seems that you can only generate a layout fragment from one NSTextElement. Besides there is only one property that associates a text element. What does it mean by 'parent element' anyway?
Posted Last updated
.
Post not yet marked as solved
0 Replies
698 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
3 Replies
1.4k 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
322 Views
Hi there, I have a small question regards to the TextKit 2 demo app: In macOS / TextDocumentView.swift / viewportBounds(for:) / Line 106 , why compare overdrawRect.minY with max(visibleRect.minY, 0)? Isn’t overdrawRect.minY always the smaller one (as it’s an overdraw rect)? Another question would be, if I'm planning to start working on an application with complex text editing features, would you recommend using NSTextView for now and wait for further updates, or just use the TextKit 2 stack (with CALayers) like the one used in the demo? I really love TextKit 2 and so far the demo seems pretty performant. But I don't know if there is any drawbacks if I build a TextKit 2 stack on my own so I'm looking forward to your advice. Many thanks
Posted Last updated
.
Post not yet marked as solved
2 Replies
651 Views
I found that compared with TextKit 1, NSTextLayoutManager does not provide any opportunity to customize line rectangles. In particular, I cannot do much about the frame of a NSTextLayoutFragment. I am curious about the proper approach to: limit the width and horizontal offset of a paragraph; add spacing below and above a paragraph; enclose a paragraph along an arbitrary path. For the first question, I noticed that in the sample app, when layout comments, BubbleLayoutFragment does not change the positions of any characters. While it did override renderingSurfaceBounds, it merely expands the bounds, without moving its text contents. And there seems to be no way to do so. For the second question, I noticed that paragraphSpacing and paragraphSpacingBefore set on the paragraph style seems to have no effect. According to the video, all layout fragments are stacked like a list. Therefore, this is an intended feature, right? Right now, my solutions to the first two questions are: If I want to control the horizontal position of a paragraph, I could simply set headIndent and trailIndent on the paragraph style. If I want to add spacing above or below a paragraph, I can add empty lines and set minimalLineHeight for the newline character. Are those the recommended ways to achieve such effect? Personally, I am not very interested in the third feature, enclosing a paragraph along an arbitrary path. However, I am indeed curious about how to add paragraph-level attachment, such as an block image/custom view between paragraphs. I think what I could do is: add an empty new line and set its height to leave enough space for the custom view; configure a CALayer or even a view and put it where the new line should be; If I want to layout characters within a strange shape, I could compute the shape's height based on the width, using TextKit 1, and then insert the new line as in step 1. Is this the intended way to approach such situation?
Posted
by selveskii.
Last updated
.
Post not yet marked as solved
1 Replies
427 Views
The way NSTextView is built it's inevitable to use NSTextStorage with TextKit2, however the NSAttributedString uses NSRange vs the TextKit2 family uses NSTextRange for text location, etc. What I struggle with is the relation between these two. I didn't find a convenient translation between these two. Is NSAttributedStrint NSRange length=1 equal to NSTextRange offset 1? I think it's not (at least it's not necessarily true for every NSTextContentManager. So my question is, given a NSTextRange, what is the corresponding NSRange in NSTextContentStorage.attributedString
Posted Last updated
.
Post marked as solved
1 Replies
537 Views
We use NSLayoutManager/NSTextStorage/NSTextContainer to display markdown attributed string on a canvas. MarkDown string was processed properly, but NSLayoutManager just exhibits the plain text. The code is as follows:     var layoutmanager = NSLayoutManager()     var textstorage = NSTextStorage()     var textcontainer = NSTextContainer()   var attrStr = try AttributedString.init(markdown: "**test**", options: AttributedString.MarkdownParsingOptions(interpretedSyntax: .inlineOnlyPreservingWhitespace)) var attrString = NSMutableAttributedString(attrStr)  self.textstorage.setAttributedString(attrString!) // draw text layout     let range = self.layoutmanager.glyphRange(for: self.textcontainer)     self.layoutmanager.drawBackground(forGlyphRange: range, at: self.textLocation)     self.layoutmanager.drawGlyphs(forGlyphRange: range, at: self.textLocation) Is it because TextKit 1 does not support markdown string display or I miss something else ? Any help will be appreciated. Thanks!
Posted
by Nuix.
Last updated
.
Post not yet marked as solved
0 Replies
296 Views
The documentation for TextKit 2 is a bit thin. I have a few questions about that: Is more complete documentation available to Developer Program members? When can developers expect for the API documentation to be updated? When will overview documentation, like TextKit 1 has, be available?
Posted
by gnuoyd.
Last updated
.
Post marked as solved
1 Replies
888 Views
Although Text will render two lines if you pass it a 2 line String, it will render the text of both lines concatenated if you pass it as a markdown string. Same if you create an AttributeText before. Examining the AT highlights the markdown structure (NSPresentationIntent, NSInlinePresentationIntent, NSLink, Header, Paragraph, CodeBlock, etc) but this is "flattened" if you will (NSLink works, multiline CodeBlock work). The problem isn't only present in Text. My use case was CoreText 2: I expected that each AttributeText run would be turned into a text layout fragment but NSTextLayoutManager also flattens my example. As an example, a screenshot of a markdown file in Xcode and the rendering in the simulator. 2 "paragraphs" aka 2 text layout fragments. The same test with the EGG recipes rtf file of the CoreText2 WWDC21 session gives predictable results, one text layout fragment per paragraph. I'm wondering if this is a bug or a "feature" of this markdown first iteration? I tried to access the NSPresentationIntent without much luck to populate CoreText2 myself. Any hint would be REALLY welcomed. thank.
Posted
by seri.
Last updated
.
Post not yet marked as solved
0 Replies
275 Views
I want to add characters to the display of my paragraphs without modifying the underlying text storage. To be concrete, let's say that I want to prefix each paragraph with "Whereas, ". Further, when I drag out a text selection on a paragraph that's displayed this way, I do not want "Whereas, " to receive a selection highlight. Using TextKit 2, it's pretty easy to accomplish the first part using a NSTextContentStorageDelegate whose method textContentStorage(_: NSTextContentStorage, textParagraphWith: NSRange) -> NSTextParagraph? adds my prefix to each paragraph. I modified the WWDC21 demo program to do it. I appreciate how I can avoid glyph munging by using TextKit 2 instead of TextKit 1, it will save a lot of trouble. If I drag a selection across the prefix, then it receives the selection highlight. What's more, when I drag a selection across the end of the paragraph, the last eight characters (and, I presume, the invisible newline) do not receive the selection highlight. So adding the prefix has skewed the selection indices. It seems to me that the TextKit 2 designers may intend for me to use a NSTextLocation subclass consisting of a "storage" index and a "display" sub-index. On the storage indices there are the characters of the text storage, and at (storage index, display sub-index)-pairs are the characters of my prefix. While the user drags out a selection, I should subtract from the NSTextSelection any NSTextRanges belonging to non-storage text such as my prefix. Am I on the right track, so far?
Posted
by gnuoyd.
Last updated
.
Post not yet marked as solved
2 Replies
429 Views
Hi! First apologies if I may have missed this in the forums somewhere, but I have been unable to find any documentation on multi-page layouts with the newer text kit and have not been able to create this using/modifying the sample code provided from the WWDC conference earlier this year. What I am trying to do is create a multipage document where the text is editable and the layout is fairly static. When the text expands in the current view to where there is no room left, a new 'page' will be created and the text will spill over there. Basically standard word or google docs type editor. The position for the pages will need be manipulated depending on some properties. I also intend in wrapping the solution in SwiftUI since the rest of the app this will be used in is a SwiftUI app. If this isn't possible, I may just stick with TextKit1 or punt altogether. Thanks! Sean
Posted
by PilotSSW.
Last updated
.
Post not yet marked as solved
1 Replies
565 Views
Hi there, I am currently trying to extend the TextKit 2 demo project from WWDC 2021 to allow keyboard input under iOS using UITextInteraction. For the most part, it is working well, but the behaviour of the directional cursor text navigation is strange: When moving up or down, it keeps the horizontal location within the current paragraph. But when crossing an empty line into another paragraph, the former horizontal position is lost and the cursor is reset to the line start. This is a video showing the behaviour: https://www.icloud.com/iclouddrive/0Xwf9hpb2DcAoU1CNPZnVgwQw#TextKit2_Selection_Bug I would expect the cursor to restore its former horizontal position in following paragraphs. This is the standard behaviour in UITextView and NSTextView. Does anybody know if this a bug in TextKit2 or if there is a way to achieve the expected beahviours. Thanks Frank
Posted
by depth42.
Last updated
.
Post not yet marked as solved
1 Replies
362 Views
I need to render some view at cursor position, how can I get the current coordinate of cursor? var location = NSRange(location: textView.selectedRange().location, length: 0) // the following is not available textLayoutManager.boundingRect(forGlyphRange: location, in: textContainer) can we use Textkit2 in production? I think there are lots of methods missing in NSTextLayoutManager while available in NSLayoutManager.
Posted
by zcius.
Last updated
.
Post not yet marked as solved
2 Replies
769 Views
I tried to wrap NSTextView using TextKit 2 into NSViewRepresentable, so I can use it on SwiftUI. But when I try to access textLayoutManager and textContentStorage, they are always nil import Foundation import SwiftUI import Cocoa struct Editor_macOS: NSViewRepresentable {     func makeNSView(context: Context) -> NSScrollView {         let textContainer = NSTextContainer(size: .zero)         context.coordinator.textLayoutManager.textContainer = textContainer         let textView = NSTextView(frame: .zero, textContainer: textContainer)         context.coordinator.textView = textView                  if let docURL = Bundle.main.url(forResource: "menu", withExtension: "rtf") {             do {                 print(textView.textLayoutManager) //                try textView.textContentStorage!.textStorage?.read(from: docURL, documentAttributes: nil, error: ())             } catch {                 // Could not read menu content.             }         }                  textView.isRichText = false         textView.font = NSFont.monospacedSystemFont(ofSize: NSFont.systemFontSize, weight: .regular)         textView.autoresizingMask = [.width]         textView.translatesAutoresizingMaskIntoConstraints = true         textView.isVerticallyResizable = true         textView.isHorizontallyResizable = false         let scrollView = NSTextView.scrollableTextView()         scrollView.hasVerticalScroller = true         scrollView.drawsBackground = false         context.coordinator.scrollView = scrollView         return scrollView     }          func updateNSView(_ nsView: NSScrollView, context: Context) {     }          class Coordinator: NSObject {         var parent: Editor_macOS         var textView: NSTextView?         let textLayoutManager = NSTextLayoutManager()         init(_ parent: Editor_macOS) {             self.parent = parent         }     }          func makeCoordinator() -> Editor_macOS.Coordinator {         Editor_macOS.Coordinator(self)     }          typealias NSViewType = NSScrollView } Does anyone have an ideal why is it?
Posted Last updated
.
Post not yet marked as solved
0 Replies
479 Views
Is it possible to use/draw a SwiftUI view in TextKit 2? The NSTextLayoutManagerDelegate seems to be built around returning a NSTextLayoutFragment. Since NSTextLayoutFragment is a class, I can't subclass it and use SwiftUI. I'd love to create my views using SwiftUI, since it's a lot faster than figuring out CGPaths, pixel perfect anti-aliasing, etc. Thanks!
Posted
by rudedogg.
Last updated
.
Post not yet marked as solved
1 Replies
446 Views
I can't get NSTextView to work in textkit2 with the following code, textView.textContentStorage , textContentStorage.textStorage is both nil. does anyone have a working example? hope there is a Frameworks Engineer help me out, appreciate so much. import AppKit open class MyEditor: NSView {   let textView: NSTextView   let textLayoutManager: NSTextLayoutManager   let textContentStorage: NSTextContentStorage   public init() {     textLayoutManager = NSTextLayoutManager()     textContentStorage = NSTextContentStorage()           let textContainer = NSTextContainer(size: NSSize(width: 200, height: 0.0))     textContainer.widthTracksTextView = true     textContainer.heightTracksTextView = true           textLayoutManager.textContainer = textContainer     textContentStorage.addTextLayoutManager(textLayoutManager)           textView = NSTextView(frame: .zero, textContainer: textContainer)               super.init(frame: .zero)           addSubview(textView)   }       public func setAttributedString(_ attrString: NSAttributedString) {     print(textView.textContentStorage) // always nil     textContentStorage.textStorage?.setAttributedString(attrString)   }       public required init?(coder: NSCoder) {     return nil   } }
Posted
by zcius.
Last updated
.