-1

The app's textView utilizes text kit 2. However the compiler reports it has to fall back to text kit 1 because text kit 1's layout manager was accessed, which was traced back to the code provided. When NSLayoutManger is not used, no warnings are reported. How can I refactor this method to resolve this issue?

Here's the code:

- (NSRange)visibleRange {
 // Get the layout manager associated with the text view
 NSLayoutManager *layoutManager = self.scriptView.layoutManager;
 
 // Get the visible rectangle of the text view
 CGRect visibleRect = self.scriptView.bounds;
 
 // Convert the visible rectangle into a glyph range
 NSRange glyphRange = [layoutManager glyphRangeForBoundingRect:visibleRect
 inTextContainer:self.scriptView.textContainer];
 
 // Convert the glyph range into a character range and return it
 return [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL];
}

This code returns the viewable range of the textview when using Mac Catalyst.

asked Jan 23, 2025 at 4:29
0

1 Answer 1

0

If I understand the goal of your visibleRange method, the following code (converted to a category on UITextView) seems to be what you are looking for using TextKit 2 code:

@interface UITextView (MyApp)
- (NSRange)visibleRange; // TextKit 1 code
- (NSRange)visibleRange2; // TextKit 2 code
@end
@implementation UITextView (MyApp)
// Your original TextKit 1 code
- (NSRange)visibleRange {
 // Get the layout manager associated with the text view
 NSLayoutManager *layoutManager = self.layoutManager;
 // Get the visible rectangle of the text view
 CGRect visibleRect = self.bounds;
 // Convert the visible rectangle into a glyph range
 NSRange glyphRange = [layoutManager glyphRangeForBoundingRect:visibleRect
 inTextContainer:self.textContainer];
 // Convert the glyph range into a character range and return it
 return [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL];
}
// Updated to use TextKit 2
- (NSRange)visibleRange2 {
 NSTextLayoutManager *layoutManager = self.textLayoutManager;
 NSTextViewportLayoutController *controller = layoutManager.textViewportLayoutController;
 // Get the text range visible in the viewport
 NSTextRange *range = controller.viewportRange;
 if (range.isEmpty) {
 return NSMakeRange(0, 0);
 } else {
 id<NSTextLocation> start = range.location;
 id<NSTextLocation> end = range.endLocation;
 // Convert the abstract text locations into offset indexes to the actual text
 NSInteger si = [layoutManager offsetFromLocation:layoutManager.documentRange.location toLocation:start];
 NSInteger ei = [layoutManager offsetFromLocation:layoutManager.documentRange.location toLocation:end];
 // Return the indexes as a start and length
 return NSMakeRange(si, ei - si);
 }
}
@end

With this category you can call visibleRange or visibleRange2 directly on your UITextView. Only call one or the other, not both. If you call visibleRange before calling visibleRange2, visibleRange2 will return an empty result due to the fallback to TextKit 1. At least this is true when running the Mac Catalyst version of the app.

Apple's sample app Using TextKit 2 to interact with text provides some useful code to help figure out this API.

answered Jan 26, 2025 at 5:48
Sign up to request clarification or add additional context in comments.

3 Comments

Great job - it really works. Thank you so much.
Glad it worked. I'm not sure what the edits were intended for. UITextView already has a textLayoutManager property. No need to add the property to the category. And it makes no sense to add a viewDidLoad method to UITextView. viewDidLoad is for a UIViewController, not a UITextView.
Unfortunately, I had a 'property not found on object of type viewController' error. However, when I specify the name of the textVIew, it now works as is: NSTextLayoutManager *layoutManager = self.textView.textLayoutManager;

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.