我正在尝试构建自定义文本视图,而我正在寻找正确方法的帮助。我是一个iOS n00b,所以主要是寻找有关如何最好地解决问题的想法。
我想构建一个自定义文本视图,其行为如下:
New Text View http://telliott.net/static/NewTextView.png
我怀疑复杂性将在识别被点击的单词中,所以让我们从那里开始。我可以想到几种方法来尝试这个:
有更明智的方法吗?你可以用其他方式检索UILabel中触及的单词吗?
如果我选择选项2,那么我认为动画来自小 - >大文本可能很复杂,因为单词将以有趣的方式包装。所以我想要另一个子视图 - 这次是UITextView - 将句子保持在小状态。将其设置为大图,然后将其隐藏,同时以每个单词的一个视图显示我的UIView。
任何想法都赞赏。在此先感谢:)
答案 0 :(得分:7)
<强>更新强>
由于在CoreText中添加了NSLayoutManager,因此iOS 7变得更加容易。如果您正在处理UITextView,则可以将布局管理器作为视图的属性进行访问。在我的情况下,我想坚持使用UILabel,所以你必须创建一个大小相同的布局管理器,即:
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:labelText];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];
CGRect bounds = label.bounds;
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:bounds.size];
[layoutManager addTextContainer:textContainer];
现在你只需要找到被点击的字符的索引,这很简单!
NSUInteger characterIndex = [layoutManager characterIndexForPoint:location
inTextContainer:textContainer
fractionOfDistanceBetweenInsertionPoints:NULL];
这使得找到这个词本身变得微不足道了:
if (characterIndex < textStorage.length) {
[labelText.string enumerateSubstringsInRange:NSMakeRange(0, textStorage.length)
options:NSStringEnumerationByWords
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if (NSLocationInRange(characterIndex, enclosingRange)) {
// Do your thing with the word, at range 'enclosingRange'
*stop = YES;
}
}];
}
原始答案,适用于iOS&lt; 7 强>
感谢@JP Hribovsek提供了一些有关此工作的提示,我设法为我的目的解决了这个问题。它感觉有点hacky,对于大型文本可能不会很好,但对于一次一段(这是我需要的),它很好。
我创建了一个简单的UILabel子类,允许我设置插入值:
#import "WWLabel.h"
#define WWLabelDefaultInset 5
@implementation WWLabel
@synthesize topInset, leftInset, bottomInset, rightInset;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.topInset = WWLabelDefaultInset;
self.bottomInset = WWLabelDefaultInset;
self.rightInset = WWLabelDefaultInset;
self.leftInset = WWLabelDefaultInset;
}
return self;
}
- (void)drawTextInRect:(CGRect)rect
{
UIEdgeInsets insets = {self.topInset, self.leftInset,
self.bottomInset, self.rightInset};
return [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}
然后我创建了一个包含我的自定义标签的UIView子类,并在点击时构建了标签中每个单词的文本大小,直到大小超过了点按位置的大小 - 这是被点击的单词。这不是完美的,但现在效果还不错。
然后我使用一个简单的NSAttributedString来突出显示文本:
#import "WWPhoneticTextView.h"
#import "WWLabel.h"
#define WWPhoneticTextViewInset 5
#define WWPhoneticTextViewDefaultColor [UIColor blackColor]
#define WWPhoneticTextViewHighlightColor [UIColor yellowColor]
#define UILabelMagicTopMargin 5
#define UILabelMagicLeftMargin -5
@implementation WWPhoneticTextView {
WWLabel *label;
NSMutableAttributedString *labelText;
NSRange tappedRange;
}
// ... skipped init methods, very simple, just call through to configureView
- (void)configureView
{
if(!label) {
tappedRange.location = NSNotFound;
tappedRange.length = 0;
label = [[WWLabel alloc] initWithFrame:[self bounds]];
[label setLineBreakMode:NSLineBreakByWordWrapping];
[label setNumberOfLines:0];
[label setBackgroundColor:[UIColor clearColor]];
[label setTopInset:WWPhoneticTextViewInset];
[label setLeftInset:WWPhoneticTextViewInset];
[label setBottomInset:WWPhoneticTextViewInset];
[label setRightInset:WWPhoneticTextViewInset];
[self addSubview:label];
}
// Setup tap handling
UITapGestureRecognizer *singleFingerTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(handleSingleTap:)];
singleFingerTap.numberOfTapsRequired = 1;
[self addGestureRecognizer:singleFingerTap];
}
- (void)setText:(NSString *)text
{
labelText = [[NSMutableAttributedString alloc] initWithString:text];
[label setAttributedText:labelText];
}
- (void)handleSingleTap:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
// Get the location of the tap, and normalise for the text view (no margins)
CGPoint tapPoint = [sender locationInView:sender.view];
tapPoint.x = tapPoint.x - WWPhoneticTextViewInset - UILabelMagicLeftMargin;
tapPoint.y = tapPoint.y - WWPhoneticTextViewInset - UILabelMagicTopMargin;
// Iterate over each word, and check if the word contains the tap point in the correct line
__block NSString *partialString = @"";
__block NSString *lineString = @"";
__block int currentLineHeight = label.font.pointSize;
[label.text enumerateSubstringsInRange:NSMakeRange(0, [label.text length]) options:NSStringEnumerationByWords usingBlock:^(NSString* word, NSRange wordRange, NSRange enclosingRange, BOOL* stop){
CGSize sizeForText = CGSizeMake(label.frame.size.width-2*WWPhoneticTextViewInset, label.frame.size.height-2*WWPhoneticTextViewInset);
partialString = [NSString stringWithFormat:@"%@ %@", partialString, word];
// Find the size of the partial string, and stop if we've hit the word
CGSize partialStringSize = [partialString sizeWithFont:label.font constrainedToSize:sizeForText lineBreakMode:label.lineBreakMode];
if (partialStringSize.height > currentLineHeight) {
// Text wrapped to new line
currentLineHeight = partialStringSize.height;
lineString = @"";
}
lineString = [NSString stringWithFormat:@"%@ %@", lineString, word];
CGSize lineStringSize = [lineString sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode];
lineStringSize.width = lineStringSize.width + WWPhoneticTextViewInset;
if (tapPoint.x < lineStringSize.width && tapPoint.y > (partialStringSize.height-label.font.pointSize) && tapPoint.y < partialStringSize.height) {
NSLog(@"Tapped word %@", word);
if (tappedRange.location != NSNotFound) {
[labelText addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:tappedRange];
}
tappedRange = wordRange;
[labelText addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:tappedRange];
[label setAttributedText:labelText];
*stop = YES;
}
}];
}
}
答案 1 :(得分:1)
UITextView已经有一个委托方法,在选择更改时触发(请注意,在textview中移动光标等同于更改选择,用户实际上不需要'选择'任何文本来调用它) :
- (void)textViewDidChangeSelection:(UITextView *)textView
每当触发此操作时,请按以下方式获取selectedRange:
NSRange range=textView.selectedRange;
如果用户应该能够手动移动光标或选择整个单词,那么你已经完成了很多工作,否则,只需在selectedRange的字符串周围添加一些处理来找出光标周围的单词是什么,并用您选择的方法突出显示它 例如,您可以枚举textview中的所有单词,并找出哪一个包含当前选择(或光标),并选择整个单词(这是突出显示之前iOS 6的一种方式)
- (void)textViewDidChangeSelection:(UITextView *)textView{
NSRange range=textView.selectedRange;
[textView.text enumerateSubstringsInRange:NSMakeRange(0, [textView.text length]) options:NSStringEnumerationByWords usingBlock:^(NSString* word, NSRange wordRange, NSRange enclosingRange, BOOL* stop){
NSRange intersectionRange=NSIntersectionRange(range,wordRange);
if(interesectionRange.length>0){
[textView setSelectedRange:wordRange];
}
}];
}