How do I make a part of a UILabel visually be a block quote?
如何使
Mail.app可以做到这一点(请参见彩色部分及其侧面的线):
如何在不使用多个
使用此总体布局(如上图)创建视图(XIB)。有一个UILabel,一个UITextView和一个UIView(蓝色矩形是设置了背景颜色的UIView)。让我们将其称为ThreadView.xib。将标签,文本视图和视图作为视图的属性进行连接。
然后我们可以有一种方法可以生成这些视图之一供我们使用,也可以有一种方法可以根据帖子有多少评论/回复将更多的ThreadView作为子视图添加。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | + (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]; } |
现在,在主视图控制器中,我们可以仅通过以下两个方法调用来创建和添加这些视图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | - (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]; } |
这将为我们提供如下图所示的结果。这段代码可以使用一些重构/重组,但这应该可以帮助您入门。您还可以混合使用自动布局和/或设置视图的框架。
这可以通过
解析html字符串(或用于标记文本的任何内容),使用自定义属性(例如
用
这是我在应用中使用的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | // 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; } |
在您的情况下,您不绘制框,而是绘制左边框。技术是一样的,希望对您有帮助!
尝试这个?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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); |
结果就是这样。
这听起来似乎违反直觉,但是您是否考虑过将所有内容都弹出到tableView中?您可以利用indentLevelAtIndexPath:stuff ....
如果您的目标iOS版本低于7,则可以使用Core Text做类似的事情,但是由于Core Text是一种旧的C opaque类型实现,因此我建议您使用DTCoreText。
如果使用> = iOS7,则可以使用NSAttributed字符串和NSXMLDocument。即使3.x提供了属性字符串,他们也仅将其添加到了iOS6的UIKIT对象中,并从根本上改变了将UIKit管理到iOS7中的行为。
NSXMLDocument很有用,因为您可以将表示它们的字符串呈现为HTML。