2014-12-14 5 views
8

Wie kann ich einen bestimmten Teil einer UILabel wie eine Blockquote aussehen, oder hat es eine vertikale Linie auf der linken Seite des Textes? Würde TextKit hier reinkommen? Wenn das so ist, wie?Wie mache ich einen Teil eines UILabel visuell zu einem Blockangebot?

Mail.app bedeutet dies (siehe die farbigen Abschnitte und die Linie auf der Seite von ihnen):

enter image description here

Wie würde ich diesen Effekt zu replizieren, ohne mehr UILabel s mit (was, wie ich bin es dynamisch zu erstellen wäre ziemlich eklig)?

Antwort

2

Wenn Sie iOS niedriger als 7 ausrichten, können Sie etwas ähnliches tun, indem Sie Core Text verwenden, aber da Core Text eine Art von alten undurchsichtigen C-Typen ist, empfehle ich Ihnen, DTCoreText zu verwenden.
Wenn Sie> = iOS7 verwenden, können Sie die NSAttributed-Zeichenfolge und NSXMLDocument verwenden. Selbst wenn eine attributierte Zeichenfolge von 3.x verfügbar ist, wurden sie nur in UIKIT-Objekte in ios6 eingefügt und das UIKit-Verhalten bei der Verwaltung in iOS7 grundlegend geändert.
NSXMLDocument ist hilfreich, da Sie Ihre Zeichenfolge als HTML darstellen können.

+0

Wie würde ich NSAttributedString dafür verwenden? –

+0

Leider habe ich kein Schnipsel dafür, das Hauptproblem wäre mit der farbigen Umrandung, die Sie einen Bildanlage-Check hier http://www.objc.io/issue-5/getting-to-know-textkit verwenden können .html – Andrea

2

Versuchen Sie das?

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

und das Ergebnis ist wie folgt.

+0

Der Schlüsselteil ist leider die Zeile auf der Seite des eingerückten Textes. Können Sie das replizieren? –

+0

Ah! Grenzen ist mit dieser Methode leider nicht möglich. –

2

Das mag kontraintuitiv klingen, aber haben Sie darüber nachgedacht, alles in einem TableView zu poppen? Sie können die indentLevelAtIndexPath ausnutzen: stuff ....

+0

Leistungsmäßig wäre das nicht vorzuziehen. –

+1

um ..... UITableView ist extrem performant! Ich weiß nicht, wie viele tausend Textzeilen du hier ansiehst, aber mit dem Zellrecycling usw. Ich bin erstaunt, dass du so denkst ... Jedenfalls zu ihren eigenen, viel Glück damit :) – Jef

5

XIB file

eine Ansicht erstellen (XIB) mit diesem allgemeinen Aufbau wie das obige Bild. Es gibt ein UILabel, ein UITextView und ein UIView (das blaue Rechteck ist ein UIView mit der Hintergrundfarbe). Nennen wir es ThreadView.xib. Haken Sie das Label, die Textansicht und die Ansicht als Eigenschaften für die Ansicht an.

Wir können dann eine Methode zum Generieren einer dieser Ansichten für uns verwenden und eine Methode zum Hinzufügen weiterer ThreadViews als Subviews basierend darauf, wie viele Kommentare/Antworten ein Beitrag hat.

+ (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]; 
} 

nun in den Hauptansicht-Controller, können wir diese Ansichten mit nur diesen beiden Methodenaufrufen erstellen und hinzufügen:

- (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]; 
} 

Dies wird uns ein Ergebnis wie im Bild unten. Dieser Code könnte etwas Refactoring/Restrukturierung verwenden, aber das sollte Ihnen den Anfang machen. Sie können auch die Verwendung des automatischen Layouts und/oder das Festlegen der Rahmen der Ansichten verwechseln.

Final result

+0

JA! ! Beschränke die Höhe deiner 'stripe'-Ansicht auf die textViews, rufe sizeToFit nach setText auf: und rufe den Bruder deines Vaters an. Sehr schlau – Jef

2

Dieses leicht mit Text Kit getan werden kann. Ich mache sowas in meiner App. Der Unterschied besteht darin, dass ich Boxen (bei Bedarf verschachtelt) verwende, um jeden Textblock zu markieren. Hier ist, was Sie tun sollten:

  1. Parse html string (oder was auch immer Sie Text verwenden markieren), markieren Sie jeden Textblock Zitat mit einem benutzerdefinierten Attribut, wie MyTextBlockAttribute, speichern Bereiche jeder Textblock (dBlock quote) und fügen Sie es als Attribut dem verwandten Bereich der attributierten Zeichenfolge hinzu (konstruieren Sie diese attributierte Zeichenfolge aus Ihrem Inhalt) und der Liste, die an den Inhalt angehängt ist. Rufen wir diese Liste MyTextBlockList.

  2. zeichnen Sie Text mit Text Kit selbst. Zeichne zuerst den Hintergrund (weiße Farbe, hellgraue Farbe usw.), zeichne Text oder vertikale Linien als nächstes. Da Sie den Bereich jedes Textblocks durch Schleifen durch die Liste abrufen können, können Sie diese Blöcke mit der Methode [NSLayoutManager range: inTextContainer:textContainer] umgrenzen.

Hier ist der Code, den ich in meiner Anwendung verwendet wird:

// 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; 
} 

In Ihrem Fall, Sie nicht Boxen zeichnen Sie Grenzen zeichnen statt links. Die Technik ist die gleiche, hoffe das kann dir helfen!

Verwandte Themen