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.
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.