Подтвердить что ты не робот

Исключить путь исключения UITextView textContainer, если полная ширина и расположение в верхней части textContainer

В iOS 8 я пытаюсь добавить UIImageView в качестве подзадачи UITextView, похожего на то, что показано здесь, но с текстом ниже изображения.

enter image description here

Я хочу сделать это с использованием пути исключения, потому что на других устройствах я мог бы поместить изображение по-разному в зависимости от размера экрана.

Однако существует ли проблема, когда CGRect, используемый для создания пути исключения, имеет Y-начало 0 и занимает всю ширину textView, исключение завершается с ошибкой и текст появляется в пути исключения (так что текст показанный за изображением, как вы можете видеть на этом снимке экрана).

Чтобы протестировать это, я построил простое приложение, используя шаблон 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, я установил такой стек UITextView в viewDidLoad():

// 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.";

Затем я настраиваю начало Y в CustomTextContainer, как это... но это не так эффектно:

- (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, о которой мне нужно сообщить (если я не пропущу что-то очевидное), но если у кого-то есть обходной путь, это будет очень признательно.

4b9b3361

Ответ 1

Это старый баг. В книге Нажатие ограничений на iOS 7 Programming автор написал это на стр. 377:

На момент написания текста Text Kit неправильно обрабатывает некоторые пути исключений. В частности, если ваши пути исключения заставят некоторые строки быть пустыми, весь макет может завершиться неудачей. Например, если вы попытаетесь выложить текст по кругу таким образом, верхняя часть круга может быть слишком мала, чтобы включать любой текст, и NSLayoutManager будет терпеть неудачу. Это ограничение влияет на все виды использования NSTextContainer. В частности, если lineFragmentRectForProposedRect: atIndex: writingDirection: ОстальноеRect: когда-либо возвращает пустой CGRect, весь макет будет терпеть неудачу.

Возможно, вы можете переопределить lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect: вашего пользовательского NSTextContainer для обхода.

Ответ 2

Я столкнулся с этой проблемой. Если вам нужно только исключить полное пространство ширины в верхней или нижней части текстового элемента, вы можете использовать textContainerInset.

Ответ 3

Обходное предложение:

Добавьте текст к вашему тексту.

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

Ответ 4

Быстрое обходное решение:

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

Ответ 5

Я слишком старался играть с путём исключения.

Моя проблема заключалась в том, что путь исключения верхнего уровня никогда не работал, он выдвигал контент вверх, а не к центру, в то время как нижний контент имел двойное поле пути исключения.

Вот что сработало для меня:

  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

Подробнее читайте в этой супер-полезной теме.