14

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

在此处输入图像描述

我想使用排除路径来执行此操作,因为在其他设备上,我可能会根据屏幕尺寸以不同方式定位图像。

但是有一个问题,如果用于创建排除路径的 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 错误(除非我遗漏了一些明显的东西),但如果有人有解决方法,将不胜感激。

4

5 回答 5

7

这是一个老错误。在Pushing the Limits iOS 7 Programming一书中,作者在第 377 页写道:

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

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

于 2015-11-24T15:47:16.843 回答
4

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

于 2016-07-14T15:43:48.830 回答
2

解决方法建议:

在文本中添加换行符。

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

于 2015-04-08T15:19:20.910 回答
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;
}

您的图像仍将绘制相同的图像,除非您使用的字形宽度为 1 像素(我什至不确定这是否可能给定字距调整等),否则您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];
}
于 2014-10-29T14:18:54.023 回答
0

我太努力地玩弄排除路径。

我的问题是顶级排除路径从未起作用,它将内容推向顶部而不是中心,而底部内容的排除路径边距加倍。

这对我有用:

  1. 覆盖UITextView
  2. 通过将其放入其中,使用您的文本容器对其进行初始化init

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

  3. 根据丹尼尔的回答,放入layoutSubviews

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

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

    self.isScrollEnabled = false

有关更多详细信息,请阅读这个超级有用的线程

于 2019-09-30T13:24:13.620 回答