UITextView NSAttributedString大小

9
我是一名有用的助手,可以为您翻译文本。

我正在开发一个使用UITextView的应用程序。

UITextView应该根据其文本内容自动调整大小,无论是垂直还是水平方向。为此,我正在子类中重写sizeToFit方法,并设置边界,如下所示:

- (void)sizeToFit {
    [self setBounds:(CGRect){.size = self.attributedText.size}];
}

问题在于这个尺寸并没有正确反映字符串的大小,因为UITextView会剪切文本。我将边缘插图设置为零,所以这不应该是一个问题,对吧?
此时,我认为这是NSAttributedString的size属性的一个bug,但如果我使用boundingRectWithSize:options:context:也会出现相同的情况。
[self setBounds:(CGRect){.size = [self.attributedText boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:0 context:nil].size}];

也许是NSAttributedString的布局计算代码与UITextView的布局计算代码不兼容。 这里有一个演示了此问题的示例项目
欢迎任何想法!
编辑:我还应该指出以下内容也不起作用:
- (void)sizeToFit {
    [self setBounds:(CGRect){.size = [self sizeThatFits:self.attributedText.size]}];
}

对于普通的文本字符串,我采用将其放入虚拟标签中,缩小标签以适应文本大小,然后获取其尺寸的方法。 - Hot Licks
3个回答

4

虽然不是完美的选择,但我最终使用了:

- (void)sizeToFit {

    CGSize textSize = self.attributedText.size;
    CGSize viewSize = CGSizeMake(textSize.width + self.firstCharacterOrigin.x * 2,
                                 textSize.height + self.firstCharacterOrigin.y * 2);

    [self setBounds:(CGRect){.size = [self sizeThatFits:viewSize]}];
}

- (CGPoint)firstCharacterOrigin {

    if (!self.text.length) return CGPointZero;

    UITextRange * range = [self textRangeFromPosition:[self positionFromPosition:self.beginningOfDocument offset:0]
                                           toPosition:[self positionFromPosition:self.beginningOfDocument offset:1]];
    return [self firstRectForRange:range].origin;
}

请添加一个检查,如果 self 具有非空文本,则仅使用您的 firstCharacterOrigin 实现,并在没有时添加 return CGPointZero。请参见此 https://dev59.com/mHA75IYBdhLWcg3weY5f#18548958。 - Stanislav Pankevich

1

我认为你是对的 - 我无法使 -[NSAttributedString boundingRectWithSize:options:context: ] 返回适用于所有字体大小的正确值...

然而,我确实可以正确地调整大小并绘制属性字符串的 UILabel:

  • 将您的文本视图类更改为 UILabel
  • 在标签上使用 -setAttributedText:
  • 设置 label.numberOfLines = 0(多行)
  • 在标签的父视图的 -layoutSubviews 中调用 -[ label sizeThatFits: ] 以获取标签的正确大小....

对我有效...

编辑:这是我的 ViewController.m 文件:

@interface View : UIView
@property ( nonatomic, strong ) UITextView * textView ;
@property ( nonatomic, strong ) UISlider * fontSizeSlider ;
@end

@implementation View

-(void)layoutSubviews
{
    CGRect bounds = self.bounds ;

    self.textView.layer.anchorPoint = (CGPoint){ 0.0f, 0.5f } ;
    self.textView.layer.position = (CGPoint){ CGRectGetMinX( bounds ), CGRectGetMidY( bounds ) } ;
    self.textView.bounds = (CGRect){ .size = [ self.textView sizeThatFits:bounds.size ] } ;

    self.fontSizeSlider.frame = CGRectMake(5, CGRectGetMaxY(bounds) - 30, bounds.size.width - 10, 25) ;
}

@end

@interface ViewController ()

@end

@implementation ViewController

-(void)loadView
{
    self.view = [[ View alloc ] initWithFrame:CGRectZero ] ;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    NSMutableAttributedString * aString = [[ NSMutableAttributedString alloc ] initWithString:@"Some test text in a scaling text view" ] ;
    NSDictionary * attributes = @{ NSForegroundColorAttributeName : [ UIColor redColor ] } ;
    [ aString setAttributes:attributes range:(NSRange){ 5, 4 } ] ;

    textView = [[UITextView alloc] initWithFrame:CGRectZero];
    [textView setAttributedText:aString ];
    [textView setFont:[UIFont systemFontOfSize:18]];
    [textView setCenter:CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds))];

    ((View*)self.view).textView = textView ;
    [self.view addSubview:textView];
    [textView release];

    UISlider * fontSizeSlider = [[UISlider alloc] initWithFrame:CGRectMake(5, CGRectGetMaxY(self.view.bounds) - 30, self.view.bounds.size.width - 10, 25)];
    [fontSizeSlider addTarget:self action:@selector(fontSizeSliderDidChange:) forControlEvents:UIControlEventValueChanged];
    [fontSizeSlider setMinimumValue:5];
    [fontSizeSlider setMaximumValue:100];
    [fontSizeSlider setValue:textView.font.pointSize];
    ((View*)self.view).fontSizeSlider = fontSizeSlider ;
    [self.view addSubview:fontSizeSlider];
    [fontSizeSlider release];
}

- (void)fontSizeSliderDidChange:(UISlider *)sender {
    [ textView setFont:[textView.font fontWithSize:sender.value]];
    [ self.view setNeedsLayout ] ;
}

@end

尽管UILabel的大小正确,但它是不可编辑的。显然,我可以在触摸下将标签切换为UITextView,但如果可能的话,除非绝对必要,我想避免这种方法。 - Tom Irving
当然可以,但是这需要同时使用UILabel和UITextView,就像我之前说的,我不介意,但是如果有一个只使用UITextView的解决方案,那就太好了。 - Tom Irving
没错,你的代码确实适应了视图的宽度,但这并不是期望的行为,它应该只足够大(宽度和高度)以适应文本,既不多也不少。 - Tom Irving
但这正是问题所在,它不起作用。请查看示例项目,尝试缩放字体大小。 - Tom Irving
我猜一直缩小它,直到它增高为止... 我更新了我的代码。 - nielsbot
显示剩余3条评论

0

Tom Irving 上面的 Swift 3 解决方案:

extension UITextView {
    func sizeToFitAttributedString() {
        let textSize = self.attributedText.size()
        let viewSize = CGSize(width: textSize.width + self.firstCharacterOrigin.x * 2, height: textSize.height + self.firstCharacterOrigin.y * 2)
        self.bounds.size = self.sizeThatFits(viewSize)
    }

    private var firstCharacterOrigin: CGPoint {
        if self.text.lengthOfBytes(using: .utf8) == 0 {
            return .zero
        }
        let range = self.textRange(from: self.position(from: self.beginningOfDocument, offset: 0)!,
                                     to: self.position(from: self.beginningOfDocument, offset: 1)!)
        return self.firstRect(for: range!).origin

    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接