Как я могу добавить разные межстрочные интервалы для разных частей NSAttributedString?

Я хочу, чтобы один NSAttributedString содержал несколько сообщений. Если в одном сообщении содержится длинный текст, и оно оборачивается, я хочу, чтобы межстрочный интервал составлял, скажем, 5. Поскольку у меня есть одна NSAttributedString, содержащая несколько сообщений, я хочу иметь больший межстрочный интервал между каждым сообщением; скажем 20.

Что я хочу

"Я вижу" - это одно сообщение. "Я думаю, это будет и то и другое" - это одно сообщение, хотя оно сводится к двум строкам, а "Как односторонний чат" - это одно сообщение.

Обратите внимание, что межстрочный интервал между 2-м и 3-м меньше, чем 1-й и 2-й и между 3-м и 4-м.

Что я пробовал

Я добавляю \n в конец каждого сообщения и пытался использовать NSParagraphStyle, который дает мне контроль межстрочного интервала, но, похоже, все или ничего:

        // index is the index of the group of messages as I iterate through them
        // contentText is an NSMutableAttributedString
        if index != messages.count - 1 {
            let style = NSMutableParagraphStyle()
            style.lineSpacing = 40.0

            let lineReturn = NSMutableAttributedString(string: "\n")
            contentText.appendAttributedString(lineReturn)

            if index == 0 {
                contentText.addAttribute(NSParagraphStyleAttributeName, value: style, range: NSMakeRange(contentText.length-lineReturn.length, lineReturn.length))
            } else {
                contentText.addAttribute(NSParagraphStyleAttributeName, value: style, range: NSMakeRange(contentText.length-lineReturn.length-1, lineReturn.length+1))
            }
        }

Если я добавлю межстрочный интервал в начало, он установит межстрочный интервал для всей метки.

        if index == 0 {
            let style = NSMutableParagraphStyle()
            style.lineSpacing = 40.0
            contentText.addAttribute(NSParagraphStyleAttributeName, value: style1, range: NSMakeRange(start, 1))
        }

(Это действительно только моя последняя попытка.)

Спасибо за любую помощь!:)

1 ответ

подробности

  • У вас есть очень простая пользовательская разметка в вашем английском сообщении, чтобы вы могли разобрать разные части

  • Поручите вашим переводчикам оставить разметку и перевести
    rest Иметь UIView, который может служить контейнером этого сообщения.

  • Разбейте свое английское сообщение на части, чтобы отделить обычный текст от текста, который можно активировать

  • Для каждого куска создайте UILabel на контейнере UIView

  • Для интерактивных элементов установите свой стиль, разрешите взаимодействие с пользователем
    и создайте свой распознаватель жестов

  • Сделайте некоторую основную бухгалтерию, чтобы поместить слова идеально по
    линии

    Для понимания.

В viewDidLoad контроллера представления я поместил это:

[self buildAgreeTextViewFromString:NSLocalizedString(@"I agree to the #<ts>terms of service# and #<pp>privacy policy#",


                                                 @"PLEASE NOTE: please translate \"terms of service\" and \"privacy policy\" as well, and leave the #<ts># and #<pp># around your translations just as in the English version of this message.")];

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

Вот метод, который соединяет сообщение - см. Ниже. Сначала я разбиваю английское сообщение над символом # (или, вернее, строкой @"#"). Таким образом, я получаю каждый кусок, для которого мне нужно создать ярлык отдельно. Я зацикливаюсь на них и ищу свою основную разметку <ts> а также <pp> определить, какие части являются ссылками на что. Если фрагмент текста, с которым я работаю, является ссылкой, то я немного стилизую и настраиваю для нее распознаватель жестов касания. Я также, конечно, убираю символы разметки. Я думаю, что это действительно простой способ сделать это.

Обратите внимание на некоторые тонкости, такие как способ обработки пробелов: я просто беру пробелы из (локализованной) строки. Если пробелов нет (китайский, японский), то между кусками также не будет пробелов. Если есть пробелы, то они автоматически распределяются по частям по мере необходимости (например, для английского). Когда мне нужно поместить слово в начале следующей строки, мне нужно убедиться, что я удаляю любой префикс пробела из этого текста, потому что в противном случае он не выравнивается должным образом.

- (void)buildAgreeTextViewFromString:(NSString *)localizedString
{
  // 1. Split the localized string on the # sign:
  NSArray *localizedStringPieces = [localizedString componentsSeparatedByString:@"#"];

  // 2. Loop through all the pieces:
  NSUInteger msgChunkCount = localizedStringPieces ? localizedStringPieces.count : 0;
  CGPoint wordLocation = CGPointMake(0.0, 0.0);
  for (NSUInteger i = 0; i < msgChunkCount; i++)
  {
    NSString *chunk = [localizedStringPieces objectAtIndex:i];
    if ([chunk isEqualToString:@""])
    {
      continue;     // skip this loop if the chunk is empty
    }

    // 3. Determine what type of word this is:
    BOOL isTermsOfServiceLink = [chunk hasPrefix:@"<ts>"];
    BOOL isPrivacyPolicyLink  = [chunk hasPrefix:@"<pp>"];
    BOOL isLink = (BOOL)(isTermsOfServiceLink || isPrivacyPolicyLink);

    // 4. Create label, styling dependent on whether it's a link:
    UILabel *label = [[UILabel alloc] init];
    label.font = [UIFont systemFontOfSize:15.0f];
    label.text = chunk;
    label.userInteractionEnabled = isLink;

    if (isLink)
    {
      label.textColor = [UIColor colorWithRed:110/255.0f green:181/255.0f blue:229/255.0f alpha:1.0];
      label.highlightedTextColor = [UIColor yellowColor];

      // 5. Set tap gesture for this clickable text:
      SEL selectorAction = isTermsOfServiceLink ? @selector(tapOnTermsOfServiceLink:) : @selector(tapOnPrivacyPolicyLink:);
      UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                                   action:selectorAction];
      [label addGestureRecognizer:tapGesture];

      // Trim the markup characters from the label:
      if (isTermsOfServiceLink) 
        label.text = [label.text stringByReplacingOccurrencesOfString:@"<ts>" withString:@""];
      if (isPrivacyPolicyLink)  
        label.text = [label.text stringByReplacingOccurrencesOfString:@"<pp>" withString:@""];
    }
    else
    {
      label.textColor = [UIColor whiteColor];
    }

    // 6. Lay out the labels so it forms a complete sentence again:

    // If this word doesn't fit at end of this line, then move it to the next
    // line and make sure any leading spaces are stripped off so it aligns nicely:

    [label sizeToFit];

    if (self.agreeTextContainerView.frame.size.width < wordLocation.x + label.bounds.size.width)
    {
      wordLocation.x = 0.0;                       // move this word all the way to the left...
      wordLocation.y += label.frame.size.height;  // ...on the next line

      // And trim of any leading white space:
      NSRange startingWhiteSpaceRange = [label.text rangeOfString:@"^\\s*"
                                                          options:NSRegularExpressionSearch];
      if (startingWhiteSpaceRange.location == 0)
      {
        label.text = [label.text stringByReplacingCharactersInRange:startingWhiteSpaceRange
                                                         withString:@""];
        [label sizeToFit];
      }
    }

    // Set the location for this label:
    label.frame = CGRectMake(wordLocation.x,
                             wordLocation.y,
                             label.frame.size.width,
                             label.frame.size.height);
    // Show this label:
    [self.agreeTextContainerView addSubview:label];

    // Update the horizontal position for the next word:
    wordLocation.x += label.frame.size.width;
  }
}

если вы хотите использовать жест, используйте этот метод.

- (void)tapOnTermsOfServiceLink:(UITapGestureRecognizer *)tapGesture
{
  if (tapGesture.state == UIGestureRecognizerStateEnded)
  {
    NSLog(@"User tapped on the Terms of Service link");
  }
}


- (void)tapOnPrivacyPolicyLink:(UITapGestureRecognizer *)tapGesture
{
  if (tapGesture.state == UIGestureRecognizerStateEnded)
  {
    NSLog(@"User tapped on the Privacy Policy link");
  }
}

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

вывод этого ответа, как на следующем снимке экрана... но вы получили представление об этом ответе.

Попался!

Вам нужно поиграть с атрибутом baselineOffset:

let contentText = NSMutableAttributedString(
string: "I see\nI'd think it`d be both a notification and a\nplace to see past announcements\nLike a one way chat.")

contentText.addAttribute(.baselineOffset, value: 10, range: NSRange(location: 0, length: 5))
contentText.addAttribute(.baselineOffset, value: -10, range: NSRange(location: 85, length: 20))


Результат:

"Я вижу

Я думаю, что это будет и уведомление, и
место, чтобы увидеть прошлые объявления

Как чат в одну сторону."

Другие вопросы по тегам