Questo può essere fatto facilmente con Text Kit
. Faccio cose del genere nella mia app. La differenza è che utilizzo le caselle (nidificate se necessario) per contrassegnare ciascun blocco di testo. Ecco cosa si dovrebbe fare:
analizzare HTML stringa (o qualsiasi altra cosa si utilizza per contrassegnare il testo), contrassegnare ogni citazione blocco di testo con un attributo personalizzato, come MyTextBlockAttribute
, salvare intervalli di ciascun blocco di testo (vale a direblock quote) e aggiungilo come attributo al relativo intervallo della stringa attribuita (costruisci questa stringa attribuita dal tuo contenuto) e l'elenco allegato al contenuto. Chiamiamo questo elenco MyTextBlockList
.
disegnare testo con Text Kit
da soli. per prima cosa disegna lo sfondo (colore bianco, colore grigio chiaro, ecc., ecc.), quindi disegna testo o linee verticali. Poiché è possibile ottenere l'intervallo di ciascun blocco di testo eseguendo un ciclo attraverso l'elenco, è possibile ottenere il limite di definizione di questi blocchi con il metodo [NSLayoutManager range: inTextContainer:textContainer]
.
Ecco il codice che ho usato nella mia app:
// 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;
}
Nel tuo caso, non si disegna scatole, peschi sinistra invece confini. La tecnica è la stessa, spero che questo possa aiutarti!
Come utilizzerei NSAttributedString per questo? –
Sfortunatamente non ho uno snippet per questo, il problema principale sarebbe con la linea di bordo colorata che puoi usare un allegato di immagine check qui http://www.objc.io/issue-5/getting-to-know-textkit .html – Andrea