ios - UITextView 的文本越界

标签 ios objective-c ios7 uitextview

我有一个不可滚动的 UITextView,它的 layoutManager maximumNumberOfLines 设置为 9,工作正常,但是,我似乎无法在 NSLayoutManager 中找到限制文本不超出 UITextView 框架的方法。

以这张截图为例,光标位于第 9 行(第 1 行被截断在截图的顶部,所以请忽略)。如果用户继续键入新字符、空格或按回车键,光标会继续离开屏幕,并且 UITextView 的字符串会继续变长。

enter image description here

我不想限制 UITextView 的字符数量,因为外来字符的大小不同。

几周来我一直在努力解决这个问题;如果有任何帮助,我将不胜感激。

自定义 TextView .h

#import <UIKit/UIKit.h>

@interface CustomTextView : UITextView <NSLayoutManagerDelegate>

@end

自定义 TextView .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 委托(delegate)方法时,我们都会调用 doesFit:string:range 函数来查看生成的文本高度是否超过 View 高度。如果是,我们返回 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 注册这些更改。请参见下面的示例(我只是复制了整个 attributedString,以便在调用撤消时将其放回原处)。

// 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];
}

现在检查委托(delegate)中的大小,如果不合适则撤消。

-(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);
        }
    }
}

关于ios - UITextView 的文本越界,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21889657/

相关文章:

ios - 正在为 iOS7 准备的应用程序中出现奇怪的导航栏

ios - 当对象不是 nil ios 时执行选择器

ios - 使用 do while 循环设置背景加载

iphone - 将 UITextField 添加到 UITableViewCell

ios - 回到iOS中的View Controller时停止播放音频

ios - UIImage 未显示在 UITableView 中

带有 Phonegap 的 iOS 7 状态栏

ios - 在带有Phonegap的IOS InAppBrowser中显示“完成”按钮

ios - 从被拒绝的证书中清除 XCode 缓存

iphone - 如何更改 entitlements.plist 的路径