0%

限制 UITextView 高度以后光标跑到最前面去了

当设置了 scrollEnabled 为 NO 以后,当输入的文字超出可显示区域以后,第一行的左上角会出现一个很小的闪烁的光标,虽然是系统的默认实现,但是看上去就像是个 bug

1
textView.scrollEnabled = NO; 

(此处省略截图)

解决思路

监听文字输入是否超出,超出以后隐藏光标,焦点在可视范围内的时候显示光标。

光标隐藏

光标创建逻辑被封装在 UITextView 内部,无法直接访问,不过我们可以通过修改 tintColor 来实现

UITextView+CaretUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@interface UITextView (CaretUtil)
@property (nonatomic, strong) UIColor *originTextViewTintColor;
- (void)updateCaretColor;
@end

#import "UITextView+CaretUtil.h"
#import <objc/runtime.h>

static void *kOriginTextViewTintColorKey = "OriginTextViewTintColor";

@implementation UITextView (CaretUtil)

- (void)updateCaretColor {
if (self.selectedTextRange) {
NSLayoutManager *layoutManager = self.layoutManager;
NSTextContainer *textContainer = self.textContainer;
UITextPosition *beginning = self.beginningOfDocument;

UITextRange *selectedRange = self.selectedTextRange;
UITextPosition *selectionStart = selectedRange.start;
UITextPosition *selectionEnd = selectedRange.end;

const NSInteger location = [self offsetFromPosition:beginning toPosition:selectionStart];
const NSInteger length = [self offsetFromPosition:selectionStart toPosition:selectionEnd];

NSRange textRange = NSMakeRange(location, length);
NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:textRange actualCharacterRange:nil];
CGRect caretRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer];
if (caretRect.size.height == 0) {
self.tintColor = [UIColor clearColor];
} else {
self.tintColor = self.originTextViewTintColor;
}
}
}

- (void)setOriginTextViewTintColor:(UIColor *)originTextViewTintColor {
objc_setAssociatedObject(self, kOriginTextViewTintColorKey, originTextViewTintColor, OBJC_ASSOCIATION_RETAIN);
}

- (UIColor *)originTextViewTintColor {
return objc_getAssociatedObject(self, kOriginTextViewTintColorKey);
}

@end

选中的时候更新光标颜色

这里直接采取继承,覆写 setSelectedTextRange: 方法,希望代码侵入性低一点的可以选择 AOP

1
2
3
4
- (void)setSelectedTextRange:(UITextRange *)selectedTextRange {
[super setSelectedTextRange:selectedTextRange];
[self updateCaretColor];
}

###TextView Delegate 中更新光标颜色

1
2
3
4
5
6
7
- (void)textViewDidChange:(UITextView *)textView {
[textView updateCaretColor];
}

- (void)textViewDidBeginEditing:(UITextView *)textView {
[textView updateCaretColor];
}