Это можно легко сделать с Text Kit
. Я делаю так, как в моем приложении. Разница заключается в том, что я использую поля (при необходимости вложенные), чтобы отмечать каждый текстовый блок. Вот то, что вы должны сделать:
Анализировать HTML строки (или что вы используете для обозначения текста), пометить каждый блок текста цитату с помощью пользовательских атрибутов, как MyTextBlockAttribute
, сохранить диапазоны каждого текстового блока (т.е.блочную кавычку) и добавьте его как атрибут в соответствующий диапазон атрибутной строки (создайте эту атрибутную строку из вашего контента) и список, прикрепленный к контенту. Позвонит этот список MyTextBlockList
.
введите текст с 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;
}
В вашем случае, вы не рисовать коробки, вы рисовать оставили границы вместо этого. Техника такая же, надеюсь, это поможет вам!
Как я могу использовать NSAttributedString для этого? –
К сожалению, у меня нет фрагмента для этого, основная проблема будет заключаться в цветной граничной линии, которую вы можете использовать для проверки прикрепления изображения здесь http://www.objc.io/issue-5/getting-to-know-textkit .html – Andrea