2014-12-14 2 views
8

Как сделать определенную часть изображения UILabel похожим на blockquote, или есть ли вертикальная линия в левой части текста? Будет ли TextKit приходить сюда? Если да, то как?Как сделать часть UILabel визуально блочной цитатой?

Mail.app делает это (см цветные участки и линии на их стороне):

enter image description here

Как бы я повторить этот эффект без использования множественного UILabel с (который, как я его динамическое создание было бы довольно грубым)?

ответ

2

Если вы ориентируетесь на iOS ниже 7, вы можете сделать что-то похожее, используя Core Text, но так как Core Text - это своего рода старая реализация непрозрачных типов C, я предлагаю вам использовать DTCoreText.
Если вы используете> = iOS7, вы можете использовать строку NSAttributed и NSXMLDocument. Даже если атрибутная строка доступна из 3.x, они только добавили их в объекты UIKIT в ios6 и радикально изменили поведение UIKit при управлении ими в iOS7.
NSXMLDocument это полезно, потому что вы можете отобразить строку, представляющую их как HTML.

+0

Как я могу использовать NSAttributedString для этого? –

+0

К сожалению, у меня нет фрагмента для этого, основная проблема будет заключаться в цветной граничной линии, которую вы можете использовать для проверки прикрепления изображения здесь http://www.objc.io/issue-5/getting-to-know-textkit .html – Andrea

2

Попробуйте?

NSString *html =[NSString stringWithFormat: 
@"<html>" 
" <head>" 
" <style type='text/css'>" 
"ul" 
"{" 
" list-style-type: none;" 
"}" 
" </style>" 
" </head>" 
" <body>" 
"%@ - PARENT" 
"<ul>" 
"<li>" 
"%@ - CHILD 1" 
"</li>" 
"<li>" 
"%@ - CHILD 2 " 
"</li>" 
"</ul>" 
"</body>" 
"</html>" 
,@"Parent Title", @"Child Description 1", @"Child Description 2"]; 


NSError *err = nil; 
_label.attributedText = 
[[NSAttributedString alloc] 
initWithData: [html dataUsingEncoding:NSUTF8StringEncoding] 
options: @{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } 
documentAttributes: nil 
error: &err]; 
if(err) 
    NSLog(@"Unable to parse label text: %@", err); 

Result

И результат, как это.

+0

Ключевой частью, к сожалению, является строка на стороне отступающего текста. Можете ли вы воспроизвести это? –

+0

Ах! Грустно это невозможно с этим методом. –

2

Это может показаться нелогичным, но вы считаете, что все это в tableView? вы можете воспользоваться indentLevelAtIndexPath: вещи ....

+0

Эффективность, которая не была бы предпочтительнее. –

+1

um ..... UITableView чрезвычайно эффективен! Я не знаю, сколько тысяч строк текста вы смотрите здесь, но с переработкой ячеек и т. Д. Я удивлен, что вы так думаете ... Во всяком случае, каждый по своему усмотрению, удачи в этом :) – Jef

5

XIB file

Создать представление (XIB) с этим генплану, как на картинке выше. Существует UILabel, UITextView и UIView (синий прямоугольник - UIView с набором цветов фона). Назовем его ThreadView.xib. Подключите ярлык, текстовое представление и представление в качестве свойств представления.

Теперь у нас есть способ сгенерировать одно из этих видов для использования и метод, который добавит больше ThreadViews в виде подзаголовков в зависимости от количества комментариев/ответов, которые есть в сообщении.

+ (instancetype)threadViewWithLabelText:(NSString *)labelText 
          textViewText:(NSString *)textViewText 
            color:(UIColor *)color 
{ 
    ThreadView *threadView = [[[NSBundle mainBundle] loadNibNamed:@"ThreadView" 
                  owner:self 
                  options:nil] firstObject]; 
    if (threadView) { 
     threadView.label.text = labelText; 
     threadView.textView.text = textViewText; 
     threadView.colorView.backgroundColor = color; 
    } 
    return threadView; 
} 

- (void)addCommentView:(ThreadView *)threadView 
     toViewController:(UIViewController *)viewController 
{ 
    threadView.frame = CGRectMake(self.frame.origin.x + 25, 
            self.textView.frame.origin.y + self.textView.frame.size.height, 
            self.frame.size.width - (self.frame.origin.x + 10), 
            self.frame.size.height - (self.textView.frame.origin.y + self.textView.frame.size.height)); 
    [viewController.view addSubview:threadView]; 
} 

Теперь в главном контроллере представления, мы можем создать и добавить эти взгляды с помощью только этих двух вызовов методы:

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // Load the first post 
    ThreadView *originalPost = [ThreadView threadViewWithLabelText:@"10 Some Words 2014 More Words" 
                 textViewText:loremIpsum 
                   color:[UIColor blueColor]]; 
    originalPost.frame = CGRectMake(self.view.frame.origin.x + 8, 
            self.view.frame.origin.y + 15, 
            self.view.frame.size.width - 8, 
            self.view.frame.size.height - 15); 
    [self.view addSubview:originalPost]; 

    // Load a comment post 
    ThreadView *commentPost = [ThreadView threadViewWithLabelText:@"12 December 2014 Maybe A Username" 
                textViewText:loremIpsum 
                  color:[UIColor greenColor]]; 
    [originalPost addCommentView:commentPost 
       toViewController:self]; 
} 

Это даст нам результат, как на рисунке. Этот код может использовать некоторые рефакторинг/реструктуризацию, но это должно вас начать. Вы также можете перемешать использование автозапуска и/или устанавливать рамки просмотров.

Final result

+0

ДА! ! ограничьте высоту вашего «полосатого» вида текстом, вызовите sizeToFit после setText: и бобы брат вашего отца. Очень умно – Jef

2

Это можно легко сделать с Text Kit. Я делаю так, как в моем приложении. Разница заключается в том, что я использую поля (при необходимости вложенные), чтобы отмечать каждый текстовый блок. Вот то, что вы должны сделать:

  1. Анализировать HTML строки (или что вы используете для обозначения текста), пометить каждый блок текста цитату с помощью пользовательских атрибутов, как MyTextBlockAttribute, сохранить диапазоны каждого текстового блока (т.е.блочную кавычку) и добавьте его как атрибут в соответствующий диапазон атрибутной строки (создайте эту атрибутную строку из вашего контента) и список, прикрепленный к контенту. Позвонит этот список MyTextBlockList.

  2. введите текст с Text Kit. сначала нарисуйте фон (белый цвет, светло-серый цвет и т. д. и т. д.), нарисуйте текст или вертикальные линии. Так как вы можете получить диапазон каждого текстового блока по циклу через список, вы можете получить ограничивающий прямоугольник этих блоков с помощью метода [NSLayoutManager range: inTextContainer:textContainer].

Вот код, который я использовал в моем приложении:

// subclass of NSTextContainer 
#import "MyTextContainer.h" 
#import "MyBlockAttribute.h" 
@interface MyTextContainer() 
@property (nonatomic) BOOL isBlock; 
@end 

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

    CGRect output = [super lineFragmentRectForProposedRect:proposedRect 
                atIndex:characterIndex 
              writingDirection:baseWritingDirection 
              remainingRect:remainingRect]; 

    NSUInteger length = self.layoutManager.textStorage.length; 

    MyTextBlockAttribute *blockAttribute; 
    if (characterIndex < length) { 
     blockAttribute = [self.layoutManager.textStorage attribute:MyTextBlockAttributeName atIndex:characterIndex effectiveRange:NULL]; // MyTextBlockAttributeName is a global NSString constant 
    } 

    if (blockAttribute) { // text block detected, enter "block" layout mode! 
     output = CGRectInset(output, blockAttribute.padding, 0.0f); // set the padding when constructing the attributed string from raw html string, use padding to control nesting, inner boxes have bigger padding, again, this is done in parsing pass 
     if (!self.isBlock) { 
      self.isBlock = YES; 
      output = CGRectOffset(output, 0.0f, blockAttribute.padding); 
     } 
    } else if (self.isBlock) { 
     self.isBlock = NO; // just finished a block, return back to the "normal" layout mode 
    } 

    // no text block detected, not just finished a block either, do nothing, just return super implementation's output 

    return output; 
} 


@end 


// drawing code, with drawRect: or other drawing technique, like drawing into bitmap context, doesn't matter 
- (void)drawBlockList:(NSArray *)blockList content:(MyContent *)content { 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGContextSetLineWidth(context, 0.5f); 
    [[UIColor colorWithWhite:0.98f alpha:1.0f] setFill]; 
    CGContextSaveGState(context); 
    MyTextContainer *textContainer = content.textContainer; 

    // since I draw boxes, I have to draw inner text block first, so use reverse enumerator 
    for (MyTextBlockAttribute *blockAttribute in [blockList reverseObjectEnumerator]) { 
     if (blockAttribute.noBackground) { // sometimes I don't draw boxes in some conditions 
      continue; 
     } 

     CGRect frame = CGRectIntegral([content.layoutManager boundingRectForGlyphRange:blockAttribute.range inTextContainer:textContainer]); 
     frame.size.width = textContainer.size.width - 2 * (blockAttribute.padding - MyDefaultMargin); // yeah... there is some margin around the boxes, like html's box model, just some simple math to calculate the accurate rectangles of text blocks 
     frame.origin.x = blockAttribute.padding - MyDefaultMargin; 
     frame = CGRectInset(frame, 0, -MyDefaultMargin); 
     if (blockAttribute.backgroundColor) { // some text blocks may have specific background color 
      CGContextSaveGState(context); 
      [blockAttribute.backgroundColor setFill]; 
      CGContextFillRect(context, frame); 
      CGContextRestoreGState(context); 
     } else { 
      CGContextFillRect(context, frame); 
     } 

     CGContextStrokeRect(context, frame); // draw borders of text blocks in the last 
    } 
    CGContextRestoreGState(context); 
} 


- (UIImage *)drawContent:(MyContent *)content { 
    UIImage *output; 
    UIGraphicsBeginImageContextWithOptions(content.bounds.size, YES, 0.0f); // bounds is calculated in other places 
    [[UIColor whiteColor] setFill]; 
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:content.bounds]; 
    [path fill]; 

    [self drawBlockList:content.blockList content:content]; // draw background first! 
    [content.layoutManager drawGlyphsForGlyphRange:NSMakeRange(0, content.textStorage.length) atPoint:CGPointZero]; // every content object has a set of Text Kit core objects, textStorage, textContainer, layoutManager 

    output = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    return output; 
} 

В вашем случае, вы не рисовать коробки, вы рисовать оставили границы вместо этого. Техника такая же, надеюсь, это поможет вам!