UITextView的文本超越界限
我有一个不可滚动的UITextView与它的layoutManager maximumNumberOfLines设置为9,这工作正常,但是,我似乎无法在NSLayoutManager中find一个方法限制文本不超过UITextView的框架。
以截图为例,光标位于第9行(第一行被截图在截图顶部,所以忽略)。 如果用户继续input新的字符,空格,或者按回车键,光标会继续离开屏幕,并且UITextView的string会持续变长。
我不想限制UITextView的字符数量,因为外部字符的大小不同。
我一直在试图解决这个问题好几个星期, 我非常感谢任何帮助。
CustomTextView.h
#import <UIKit/UIKit.h> @interface CustomTextView : UITextView <NSLayoutManagerDelegate> @end
CustomTextView.m
#import "CustomTextView.h" @implementation CustomTextView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = [UIColor clearColor]; self.font = [UIFont systemFontOfSize:21.0]; self.dataDetectorTypes = UIDataDetectorTypeAll; self.layoutManager.delegate = self; self.tintColor = [UIColor companyBlue]; [self setLinkTextAttributes:@{NSForegroundColorAttributeName:[UIColor companyBlue]}]; self.scrollEnabled = NO; self.textContainerInset = UIEdgeInsetsMake(8.5, 0, 0, 0); self.textContainer.maximumNumberOfLines = 9; } return self; } - (CGFloat)layoutManager:(NSLayoutManager *)layoutManager lineSpacingAfterGlyphAtIndex:(NSUInteger)glyphIndex withProposedLineFragmentRect:(CGRect)rect { return 4.9; } @end
更新,仍然没有解决
我想这是一个更好的答案。 每当调用shouldChangeTextInRange委托方法时,我们调用我们的doesFit:string:range函数来查看生成的文本高度是否超过视图高度。 如果是这样,我们返回NO来防止发生变化。
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { FLOG(@" called"); // allow deletes if (text.length == 0) return YES; // Check if the text exceeds the size of the UITextView return [self doesFit:textView string:text range:range]; } - (float)doesFit:(UITextView*)textView string:(NSString *)myString range:(NSRange) range; { // Get the textView frame float viewHeight = textView.frame.size.height; float width = textView.textContainer.size.width; NSMutableAttributedString *atrs = [[NSMutableAttributedString alloc] initWithAttributedString: textView.textStorage]; [atrs replaceCharactersInRange:range withString:myString]; NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:atrs]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize: CGSizeMake(width, FLT_MAX)]; NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; [layoutManager addTextContainer:textContainer]; [textStorage addLayoutManager:layoutManager]; float textHeight = [layoutManager usedRectForTextContainer:textContainer].size.height; FLOG(@" viewHeight = %f", viewHeight); FLOG(@" textHeight = %f", textHeight); if (textHeight >= viewHeight - 1) { FLOG(@" textHeight >= viewHeight - 1"); return NO; } else return YES; }
编辑确定您还需要添加一些检查,如果您更改文本的格式。 在我的情况下,用户可以改变字体或使其粗体,改变段落样式等等。所以现在任何这些改变也可能导致文本超过textView边框。
所以首先你需要确保你使用textViews的undoManager来注册这些改变。 看下面的例子(我只是复制整个归属string,所以我可以把它撤回,如果撤消称为)。
// This is in my UITextView subclass but could be anywhere // This gets called to undo any formatting changes - (void)setMyAttributedString:(NSAttributedString*) atstr { self.attributedText = atstr; self.selectedRange = _undoSelection; } // Before we make any format changes save the attributed string with undoManager // Also save the current selection (maybe should save this with undoManager as well using a custom object containing selection and attributedString) - (void)formatText:(id)sender { //LOG(@"formatText: called"); NSAttributedString *atstr = [[NSAttributedString alloc] initWithAttributedString:self.textStorage]; [[self undoManager] registerUndoWithTarget:self selector:@selector(setMyAttributedString:) object:atstr]; // Remember selection _undoSelection = self.selectedRange; // Add text formatting attributes ... // Now tell the delegate that something changed [self.delegate textViewDidChange:self]; }
现在检查委托中的大小,如果不合适,则撤消。
-(void)textViewDidChange:(UITextView *)textView { FLOG(@" called"); if ([self isTooBig:textView]) { FLOG(@" text is too big so undo it!"); @try { [[textView undoManager] undo]; } @catch (NSException *exception) { FLOG(@" exception undoing things %@", exception); } } }
boundingRectWithSize:options:attributes:context:
不build议使用textviews,因为它不会采用textview的各种属性(如填充),因此会返回不正确或不精确的值。
要确定文本usedRectForTextContainer:
的文本大小,请使用布局pipe理器的usedRectForTextContainer:
和textview的文本容器来获取文本所需的精确矩形,并考虑到所有必需的布局约束和textview怪异。
CGRect rect = [self.textView.layoutManager usedRectForTextContainer:self.textView.textContainer];
我build议在processEditingForTextStorage:edited:range:changeInLength:invalidatedRange:
调用super
实现之后执行此操作。 这将意味着通过提供自己的文本容器并将其布局pipe理器设置为您的子类的实例来replacetextview的布局pipe理器。 这样你就可以提交用户所做的textview的更改,检查rect是否仍然可以接受,如果不可以,则取消。
你将需要自己做这个。 基本上它会这样工作:
- 在你的
UITextViewDelegate
的textView:shouldChangeTextInRange:replacementText:
方法中查找当前文本的大小(例如NSStringsizeWithFont:constrainedToSize:
textView:shouldChangeTextInRange:replacementText:
。 - 如果大小大于允许返回FALSE,则返回TRUE。
- 如果用户input的内容超出允许的范围,请向用户提供自己的反馈意见。
编辑:由于sizeWithFont:
不推荐使用boundingRectWithSize:options:attributes:context:
例:
NSString *string = @"Hello World"; UIFont *font = [UIFont fontWithName:@"Helvetica-BoldOblique" size:21]; CGSize constraint = CGSizeMake(300,NSUIntegerMax); NSDictionary *attributes = @{NSFontAttributeName: font}; CGRect rect = [string boundingRectWithSize:constraint options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes:attributes context:nil];
我创build了一个testingVC。 每次在UITextView中到达新行时,它都会增加一个行计数器。 据我所知,你想限制你的文字input不超过9行。 我希望这回答了你的问题。
#import "ViewController.h" @interface ViewController () @property IBOutlet UITextView *myTextView; @property CGRect previousRect; @property int lineCounter; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self.myTextView setDelegate:self]; self.previousRect = CGRectZero; self.lineCounter = 0; } - (void)textViewDidChange:(UITextView *)textView { UITextPosition* position = textView.endOfDocument; CGRect currentRect = [textView caretRectForPosition:position]; if (currentRect.origin.y > self.previousRect.origin.y){ self.lineCounter++; if(self.lineCounter > 9) { NSLog(@"Reached line 10"); // do whatever you need to here... } } self.previousRect = currentRect; } @end
您可以检查边界矩形的大小,如果太大,请调用撤消pipe理器撤消最后一个操作。 可以是粘贴操作或input文本或换行符。
这是一个快速入侵,检查文本的高度是否太接近textView的高度。 还要检查textView矩形是否包含文本矩形。 您可能需要更多地摆弄这个以适应您的需求。
-(void)textViewDidChange:(UITextView *)textView { if ([self isTooBig:textView]) { FLOG(@" too big so undo"); [[textView undoManager] undo]; } } /** Checks if the frame of the selection is bigger than the frame of the textView */ - (bool)isTooBig:(UITextView *)textView { FLOG(@" called"); // Get the rect for the full range CGRect rect = [textView.layoutManager usedRectForTextContainer:textView.textContainer]; // Now convert to textView coordinates CGRect rectRange = [textView convertRect:rect fromView:textView.textInputView]; // Now convert to contentView coordinates CGRect rectText = [self.contentView convertRect:rectRange fromView:textView]; // Get the textView frame CGRect rectTextView = textView.frame; // Check the height if (rectText.size.height > rectTextView.size.height - 16) { FLOG(@" rectText height too close to rectTextView"); return YES; } // Find the intersection of the two (in the same coordinate space) if (CGRectContainsRect(rectTextView, rectText)) { FLOG(@" rectTextView contains rectText"); return NO; } else return YES; }
另一种select – 在这里我们检查大小,如果它太大,防止任何新的字符被input,除非它的删除。 不漂亮,因为这也防止如果超过高度在顶部填充线。
bool _isFull; -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { FLOG(@" called"); // allow deletes if (text.length == 0) return YES; // Check if the text exceeds the size of the UITextView if (_isFull) { return NO; } return YES; } -(void)textViewDidChange:(UITextView *)textView { FLOG(@" called"); if ([self isTooBig:textView]) { FLOG(@" text is too big!"); _isFull = YES; } else { FLOG(@" text is not too big!"); _isFull = NO; } } /** Checks if the frame of the selection is bigger than the frame of the textView */ - (bool)isTooBig:(UITextView *)textView { FLOG(@" called"); // Get the rect for the full range CGRect rect = [textView.layoutManager usedRectForTextContainer:textView.textContainer]; // Now convert to textView coordinates CGRect rectRange = [textView convertRect:rect fromView:textView.textInputView]; // Now convert to contentView coordinates CGRect rectText = [self.contentView convertRect:rectRange fromView:textView]; // Get the textView frame CGRect rectTextView = textView.frame; // Check the height if (rectText.size.height >= rectTextView.size.height - 10) { return YES; } // Find the intersection of the two (in the same coordinate space) if (CGRectContainsRect(rectTextView, rectText)) { return NO; } else return YES; }
在IOS 7中有一个新类与UITextviews一起工作,这是NSTextContainer类
它通过Textviews文本容器属性与UITextview一起使用
它有这个属性称为大小…
大小控制接收器边界矩形的大小。 默认值:CGSizeZero。
@property(nonatomic)CGSize size Discussion该属性定义从lineFragmentRectForProposedRect返回的布局区域的最大尺寸:atIndex:writingDirection:remainingRect :. 0.0或更小的值意味着没有限制。
我仍然在理解和尝试,但我相信它应该可以解决您的问题。
无需查找行数。 我们可以通过从textview中计算光标位置来获得所有这些东西,并根据这个我们可以根据UITextView的高度最小化UITextView的UIFont。
这里是link.Please请参阅此。 https://github.com/jayaprada-behera/CustomTextView