如果全宽并且位于textContainer顶部,则UITextView textContainer排除路径将失败

时间:2014-10-29 14:07:58

标签: ios uitextview nstextcontainer

在iOS 8中,我尝试将UIImageView添加为UITextView的子视图,类似于此处显示的内容 - 但图像下方的文字。

enter image description here

我想使用排除路径来执行此操作,因为在其他设备上,我可能会根据屏幕大小对图像进行不同的定位。

然而,如果用于创建排除路径的CGRect的Y原点为0并占用textView的整个宽度,则排除失败并且文本出现在排除路径中(如此文本显示在imageView后面,如截图所示。

为了测试这个,我使用"单一视图"构建了一个简单的应用程序。 Xcode模板,包含以下内容:

- (void)viewDidLoad {
    [super viewDidLoad];

    // set up the textView
    CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
    UITextView *textView = [[UITextView alloc] initWithFrame:frame];
    [textView setFont:[UIFont systemFontOfSize:36.0]];
    [self.view addSubview:textView];
    textView.text = @"I wish this text appeared below the exclusion rect and not within it.";


    // add the photo

    CGFloat textViewWidth = textView.frame.size.width;

    // uncomment if you want to see that the exclusion path DOES work when not taking up the full width:
    // textViewWidth = textViewWidth / 2.0;

    CGFloat originY = 0.0;

    // uncomment if you want to see that the exclusion path DOES work if the Y origin isn't 0
    // originY = 54.0;

    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"photo_thumbnail"]];
    imageView.frame = CGRectMake(0, originY, textViewWidth, textViewWidth);
    imageView.alpha = 0.7; // just so you can see that the text is within the exclusion path (behind photo)
    [textView addSubview:imageView];


    // set the exclusion path (to the same rect as the imageView)
    CGRect exclusionRect = [textView convertRect:imageView.bounds fromView:imageView];
    UIBezierPath *exclusionPath = [UIBezierPath bezierPathWithRect:exclusionRect];
    textView.textContainer.exclusionPaths = @[exclusionPath];
}

我还尝试了对NSTextContainer进行子类化并覆盖-lineFragmentRectForProposedRect方法,但调整Y原点似乎也没有帮助。

要使用自定义NSTextContainer,我在viewDidLoad()中设置了这样的UITextView堆栈:

// set up the textView stack
NSTextStorage *textStorage = [[NSTextStorage alloc] init];

NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];

CGSize containerSize = CGSizeMake(self.view.frame.size.width, CGFLOAT_MAX);
CustomTextContainer *textContainer = [[CustomTextContainer alloc] initWithSize:containerSize];
[layoutManager addTextContainer:textContainer];

CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
UITextView *textView = [[UITextView alloc] initWithFrame:frame textContainer:textContainer];
[textView setFont:[UIFont systemFontOfSize:36.0]];
[self.view addSubview:textView];

textView.text = @"I wish this text appeared below the exclusion rect and not within it.";

然后我像这样调整CustomTextContainer中的Y原点......但是这个失败的原因非常明显:

- (CGRect)lineFragmentRectForProposedRect:(CGRect)proposedRect atIndex:(NSUInteger)characterIndex writingDirection:(NSWritingDirection)baseWritingDirection remainingRect:(CGRect *)remainingRect {

    CGRect correctedRect = proposedRect;

    if (correctedRect.origin.y == 0) {
        correctedRect.origin.y += 414.0;
    }

    correctedRect = [super lineFragmentRectForProposedRect:correctedRect atIndex:characterIndex writingDirection:baseWritingDirection remainingRect:remainingRect];

    NSLog(@"origin.y: %f | characterIndex: %lu", correctedRect.origin.y, (unsigned long)characterIndex);

    return correctedRect;
}

我认为这可以被视为我需要报告的Apple漏洞(除非我遗漏了一些明显的东西),但是如果有人有解决方法,我将非常感激。

5 个答案:

答案 0 :(得分:6)

这是一个老版本的错误。在 推动限制iOS 7编程 一书中,作者在第377页写道:

  

在撰写本文时,Text Kit无法正确处理某些类型的排除路径。特别是,如果您的排除路径会强制某些行为空,则整个布局可能会失败。例如,如果您尝试以这种方式在圆圈中布置文本,则圆圈的顶部可能太小而无法包含任何文本,并且NSLayoutManager将无声地失败。此限制会影响NSTextContainer的所有用途。具体来说,如果lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:永远返回一个空的CGRect,整个布局将失败。

也许你可以覆盖lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:   您的自定义NSTextContainer的解决方法。

答案 1 :(得分:3)

我也遇到过这个问题。如果您只需要排除textView顶部或底部的全宽空格,则可以使用textContainerInset

答案 2 :(得分:2)

变通方法建议:

在文字中添加换行符。

textView.text = "\n" + textView.text

答案 3 :(得分:1)

快速解决方法:

CGRect exclusionRect = [textView convertRect:imageView.bounds fromView:imageView];
if (exclusionRect.origin.x <= 0 && exclusionRect.origin.y <= 0 && exclusionRect.size.width >= textView.bounds.size.width) {
  exclusionRect.origin.x = 1;
  exclusionRect.origin.y = 1;
  exclusionRect.size.width -= 2;
}

你的图像仍会绘制相同的图像,除非你使用的字体宽度为1px(我甚至不确定是否可以通过字距调整等),你的exclusionRect将保证是小于全宽。

如果您允许实时移动矩形,我很想知道您会看到什么样的结果。附加UIPanGestureRecognizer并在屏幕上平移时更新exclusionRect。文本在什么时候跳转到图像中?

编辑:如果您遇到问题,直到它能够容纳至少一个字符,则可以尝试调整文本框架。

if (exclusionRect.origin.x <= 0 && exclusionRect.origin.y <= 0 && exclusionRect.size.width >= textView.bounds.size.width) {
  frame.origin.y += CGRectGetMaxY(exclusionRect);
  frame.size.height -= CGRectGetMaxY(exclusionRect);
  [textView setFrame:frame];
}

答案 4 :(得分:0)

我尝试用排除路径太努力了。

我的问题是,顶层排除路径从不起作用,它将内容推向顶部而不是中心,而底部内容的排除路径余量增加了一倍。

以下是对我有用的:

  1. 覆盖UITextView
  2. 通过将其放入init中来与您的文本容器初始化:

    super.init(frame: frame, textContainer: textContainer)

  3. 根据Daniel's answer,放在layoutSubviews内:

    self.textContainerInset = UIEdgeInsets.init(top: t, left: l, bottom: b, right: r)

  4. UITextView子类init()内或情节提要中,禁用滚动。

    self.isScrollEnabled = false

有关更多详细信息,请阅读this super-helpful thread