问题
I have a custom UITextView
with custom line spacing applied. When I try to select the text the selectionRect
is wrong. Check this image where highlighting is correct but the size of the handles at selectionRange start and end is wrong. That particular line has beforeSpacing of 50px and afterSpacing of 10px applied.
Instead I want it to behave like this
I modified the cursor size using caretRectForPosition:
and modified the position and size of the cursor by changing its rect, but unfortunately this does not affect the handles during selection.
How do I modify the selectionRect or the size of the selection handles based on my font size and line spacing that is applied?
回答1:
TL;DR:
You can use -(NSArray *)selectionRectsForRange
, which behaves weird and not documented very well. The last two rectangles returned by UITextView
when calling -(NSArray *)selectionRectsForRange
have zero width, and they determine the height of the begin and end cursors. Create a subclass, override the method, call super and modify the height of the last two rects. To be able to modify them, you need to create a subclass of UITextSelectionRect
because the original version is not writable (see the end of this answer).
Long version:
The way this method is implemented in UITextView
is weird. Here is what I figured by trial and error:
If you subclass UITextView, and override the method like this:
- (NSArray *)selectionRectsForRange:(UITextRange *)range
{
NSArray* result = [super selectionRectsForRange:range];
NSLog(@"%@", result);
return result;
}
you will see the method returns a set of rectangles that span the selection, but also two rectangles with width zero, that coincide with the location of the cursor.
Interestingly enough, changing the order of the array does not have any impact on the selection or the cursor positions, so there is no need to make these rectangles the last two, it's rather a detail of apples implementation. Removing them all together has a more interesting effect: the cursors don't disappear, and neither do any of the selection rectangles. Rather, the cursors take the height of the adjacent rectangle. When selecting a whole paragraph of text, this leads to the cursors spanning the height of the whole paragraph. My conclusion is, that the cursors orient themselves towards the height and position of the upper-leftets/lower-rightest rects in the selection, and Apples implementation of -(NSArray *)selectionRectsForRange
tricks this system by inserting zero-width rectangles. This is by no means certain, and there could be some more intricacies to the system, concerning text direction and other quirks. I tested my hypothesis on iOS 8 and 10 on device and in the simulator.
Bonus this is my mutable UITextSelectionRect subclass:
@interface RichTextSelectionRect : UITextSelectionRect
//Prefix everything with _ because the original names are marked as readonly in the superclass
@property (nonatomic) CGRect _rect;
@property (nonatomic) UITextWritingDirection _writingDirection;
@property (nonatomic) BOOL _containsStart; // Returns YES if the rect contains the start of the selection.
@property (nonatomic) BOOL _containsEnd; // Returns YES if the rect contains the end of the selection.
@property (nonatomic) BOOL _isVertical; // Returns YES if the rect is for vertically oriented text.
@end
@implementation RichTextSelectionRect
- (CGRect)rect{
return __rect;
}
- (UITextWritingDirection)writingDirection{
return __writingDirection;
}
- (BOOL)containsStart
{
return __containsStart;
}
- (BOOL)containsEnd
{
return __containsEnd;
}
- (BOOL)isVertical
{
return __isVertical;
}
@end
来源:https://stackoverflow.com/questions/38199591/text-selection-not-happening-properly-because-of-custom-line-spacing-in-uitextvi