Text selection not happening properly because of custom line spacing in UITextView

扶醉桌前 提交于 2020-01-03 03:01:13

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!