由于 UITextView 中的自定义行间距,文本选择不正确

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

我有一个应用了自定义行距的自定义 UITextView。当我尝试 select 文本时 selectionRect 是错误的。 Check this image 其中突出显示是正确的,但 selectionRange 开始和结束处的句柄大小是错误的。该特定行应用了 50px 的 beforeSpacing 和 10px 的 afterSpacing。

相反,我希望它表现得像 this

我使用 caretRectForPosition: 修改了光标大小,并通过更改其矩形修改了光标的位置和大小,但不幸的是,这不会影响 selection 期间的句柄。

如何根据我应用的字体大小和行间距修改 selectionRect 或 selection 句柄的大小?

长话短说: 您可以使用 -(NSArray *)selectionRectsForRange,它的行为很奇怪并且没有很好地记录。调用 -(NSArray *)selectionRectsForRangeUITextView 返回的最后两个矩形的宽度为零,它们决定了开始和结束游标的高度。创建一个子类,覆盖方法,调用super并修改最后两个rects的高度。为了能够修改它们,您需要创建 UITextSelectionRect 的子类,因为原始版本不可写(请参阅此答案的末尾)。

长版: 这个方法在UITextView中的实现方式很奇怪。这是我通过反复试验得出的结论:

如果您将 UITextView 子类化,并重写这样的方法:

- (NSArray *)selectionRectsForRange:(UITextRange *)range
{
    NSArray* result = [super selectionRectsForRange:range];
    NSLog(@"%@", result);
    return result;
}

您将看到方法 returns 一组横跨选区的矩形,还有两个宽度为零且与光标位置重合的矩形。

有趣的是,更改数组的顺序对选择或光标位置没有任何影响,因此无需将这些矩形设为最后两个,而是 apples 实现的细节。将它们一起移除会产生更有趣的效果:光标不会消失,任何选择矩形也不会消失。相反,光标采用相邻矩形的高度。选择整段文本时,这会导致光标跨越整段的高度。我的结论是,光标将自己定向到选择中 upper-leftets/lower-rightest 矩形的高度和位置,Apple 的 -(NSArray *)selectionRectsForRange 实现通过插入零宽度矩形来欺骗该系统。这绝不是确定的,并且系统可能会有一些更复杂的问题,涉及文本方向和其他怪癖。我在设备和模拟器中对 iOS 8 和 10 测试了我的假设。

奖金 这是我的可变 UITextSelectionRect 子类:

@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