Take a look at the renderings first

You can specify the width of the display, the height of the display automatically increases with the number of words and it’s not fun to see that, that’s basically how IM chats work. The height of the View is automatically and dynamically increased as the font size is automatically increased

:Here's coreText. Here's a quote from Tang Qiao's blog

CoreText is the underlying technology for processing text and fonts. It works directly with Core Graphics (also known as Quartz). Quartz is a 2D graphics rendering engine capable of handling graphical displays in OSX and iOS. Quartz works directly with fonts (font) and glyphs, rendering text to the interface, and is the only module in the base library capable of handling glyphs. Therefore, CoreText needs to pass the displayed text content, position, font, and glyphs directly to Quartz for typography purposes. Compared to other UI components, CoreText provides faster typography because it interacts directly with Quartz. CoreText is very low-level, and the top UI controls (including UILabel, UITextField and UITextView) and UIWebView are all based on CoreText.

MVC concept to implement

There are three classes in model

YYGFrameParserConfig YYGFrameParser YYGCoreTextDat

Model

1. YYGFrameParserConfig class

Now that you want to present the content, you must know how to present the content of the content! That’s what this class does.

You need to know the width to display

@property(nonatomic, assign) CGFloat width;

You need to know the font size to display

@property(assign,nonatomic) CGFloat fontSize;

You need to know the line distance to display

@property(assign,nonatomic) CGFloat lineSpace;

You need to know the font color to display

@property(strong,nonatomic) UIColor * textColor;

The following code


@interface YYGFrameParserConfig : NSObject

/** * The width of the view */
@property(nonatomic.assign) CGFloat width;

/** * font size */
@property(assign.nonatomic) CGFloat fontSize;

/** ** line distance */
@property(assign.nonatomic) CGFloat lineSpace;

/** * font color */
@property(strong.nonatomic) UIColor * textColor;


@end-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --. M files#import "YYGFrameParserConfig.h"

@implementation YYGFrameParserConfig- (id)init
{
    self= [super init];
    if (self)
    {
        _width=200.0f;
        _fontSize=16.0f;
        _lineSpace=8.0f;
        _textColor=RGB(108.108.108);
    }
    return self;
}

@endCopy the code

2. YYGFrameParser class

We all know that any information displayed on the phone screen is done by the underlying graphics rendering engine, while in UIView it is drawn on the canvas by CTFrameDraw(CTFrameRef, CGContextRef). So this class is the one that generates CTFrameRef. Generate these to display on the View! You need content! And some constraints on the content presentation, width, font size, which we did in the last class! So the following is needed:

Need to content

Need YYGFrameParserConfig

And as I said at the beginning, you can dynamically produce height through content and that’s the key so you need to return height and generated CTFrameRef

Returns the height

Return CTFrameRef

It returns two arguments. Okay, let’s just make another model so we have the next model class

Code of this class:

#import "YYGFrameParserConfig.h"
#import "YYGCoreTextData.h"


@interface YYGFrameParser : NSObject

+(YYGCoreTextData *)parseContent:(NSString *)content config:(YYGFrameParserConfig *)config;

@end.m files#import "YYGFrameParser.h"

@implementation YYGFrameParser

+(YYGCoreTextData *)parseContent:(NSString *)content config:(YYGFrameParserConfig *)config
{
    NSDictionary * dictionary=[self attributesWithConfig:config];
    NSAttributedString * string=[[NSAttributedString alloc]initWithString:content attributes:dictionary];
    // Create CTFramesetterRef instance
    CTFramesetterRef framesetter=CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);
    // Get the height of the region to draw
    CGSize restrictSize=CGSizeMake(config.width, CGFLOAT_MAX);
    CGSize coreTextSize=CTFramesetterSuggestFrameSizeWithConstraints(framesetter,CFRangeMake(0.0), nil, restrictSize, nil);
    CGFloat textHeight=coreTextSize.height;

     // Generate a CTFrameRef instance
    CTFrameRef frameRef=[self createFrameWithFramesetter:framesetter config:config height:textHeight];
    // Save the generated CTFrameRef instance and calculated draw height to the CoreTextData instance, and return the CoreTextData instance
    YYGCoreTextData *data=[[YYGCoreTextData alloc]init];
    data.ctFram=frameRef;
    data.height=textHeight;

    CFRelease(frameRef);
    CFRelease(framesetter);

    returndata; } + (NSDictionary *)attributesWithConfig:(YYGFrameParserConfig *)config
{

    NSMutableDictionary * dict=[NSMutableDictionary dictionary];
    / / 1
    CGFloat fontSize=config.fontSize;
    CTFontRef fontRef=CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);
    dict[(id)kCTFontAttributeName]=(__bridge id)fontRef;

    CFRelease(fontRef);
    / / 2
    CGFloat lineSpace=config.lineSpace;
    const CFIndex kNumberOfSetting=3;
    CTParagraphStyleSetting theSettings[kNumberOfSetting]={
        {kCTParagraphStyleSpecifierLineSpacingAdjustment,sizeof(CGFloat),&lineSpace},
        {kCTParagraphStyleSpecifierMaximumLineSpacing,sizeof(CGFloat),&lineSpace},
        {kCTParagraphStyleSpecifierMinimumLineSpacing,sizeof(CGFloat),&lineSpace}
    };
    CTParagraphStyleRef thePragraphRef=CTParagraphStyleCreate(theSettings, kNumberOfSetting);
    dict[(id)kCTParagraphStyleAttributeName]=(__bridge id)thePragraphRef;

    CFRelease(thePragraphRef);
    / / 3
    UIColor * textColor=config.textColor;
    dict[(id)kCTForegroundColorAttributeName]=(__bridge id)textColor.CGColor;

    return dict;
}

+(CTFrameRef )createFrameWithFramesetter:(CTFramesetterRef)framesetter config:(YYGFrameParserConfig *)config height:(CGFloat)height
{
    CGMutablePathRef path=CGPathCreateMutable(a);CGPathAddRect(path, NULL.CGRectMake(0.0, config.width, height));

    CTFrameRef frame=CTFramesetterCreateFrame(framesetter, CFRangeMake(0.0), path, NULL);

    CFRelease(path);

    return frame;
}



@endCopy the code

3.YYGCoreTextData

The reason for its appearance comes from the introduction above

Store the produced CTFrameRef

@property(assign,nonatomic) CTFrameRef ctFram;

Height of dynamic generation of storage production

@property(assign,nonatomic) CGFloat height;

.m files#import "YYGCoreTextData.h"

@implementation YYGCoreTextData- (void)setCtFram:(CTFrameRef)ctFram
{
    if(_ctFram! =ctFram) {if(_ctFram! =nil)
        {
            CFRelease(_ctFram);
        }
        CFRetain(ctFram);
        _ctFram=ctFram;
    }
}

-(void)dealloc
{
    if(_ctFram! =nil)
    {
        CFRelease(_ctFram);
        _ctFram=nil; }}@endCopy the code

View

.h files@property(strong.nonatomic) YYGCoreTextData * data; .m file - (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    CGContextRef context=UIGraphicsGetCurrentContext(a);CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0.self.bounds.size.height);
    CGContextScaleCTM(context, 1.0.1.0);
    if (self.data)
    {
        CTFrameDraw(self.data.ctFram, context); }}Copy the code

Well, you also need to go from writing UIViewCategorymethods

Here’s a class

#import <UIKit/UIKit.h>

@interface UIView (YYGView)- (CGFloat)x; - (void)setX:(CGFloat)x; - (CGFloat)y; - (void)setY:(CGFloat)y; - (CGFloat)wide; - (void)setWide:(CGFloat)wide; - (CGFloat)height; - (void)setHeight:(CGFloat)height;

@end.m files#import "UIView+YYGView.h"

@implementation UIView (YYGView)- (CGFloat)x
{
    return self.frame.origin.x; } - (void)setX:(CGFloat)x
{
    self.frame=CGRectMake(x, self.y, self.wide, self.height); } - (CGFloat)y
{
    return self.frame.origin.y; } - (void)setY:(CGFloat)y
{
    self.frame=CGRectMake(self.x, y, self.wide, self.height); } - (CGFloat)wide
{
    return self.frame.size.width; } - (void)setWide:(CGFloat)wide
{
    self.frame=CGRectMake(self.x, self.y, wide, self.height); } - (CGFloat)height
{
    return self.frame.size.height; } - (void)setHeight:(CGFloat)height
{
    self.frame=CGRectMake(self.x, self.y, self.wide, height);
}


@endCopy the code

YYGDisplayView class

The method of Model and UIview category is to draw things on the screen, which is to play auxiliary for YYGDisplayView, auxiliary is very important!!

.m files#import "YYGDisplayView.h"

@implementation YYGDisplayView

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    CGContextRef context=UIGraphicsGetCurrentContext(a);CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0.self.bounds.size.height);
    CGContextScaleCTM(context, 1.0.1.0);
    if (self.data)
    {
        CTFrameDraw(self.data.ctFram, context); }}@endCopy the code

The Controller is now called

    YYGFrameParserConfig * config=[[YYGFrameParserConfig alloc]init];
    config.textColor=[UIColor purpleColor];
    config.width=self.YYGView.wide;

    YYGCoreTextData * data=[YYGFrameParser parseContent:The South Korea-U.S. Joint Chiefs of Staff said Wednesday that North Korea fired two suspected Rodong ballistic missiles toward the Sea of Japan at 7:50 a.m. One of the missiles exploded shortly after launch, while the other flew over North Korea and landed in Japan's exclusive economic zone 250 kilometers off the Noka Peninsula in Akita Prefecture, a total distance of about 1,000 kilometers. Japan's Kyodo news agency analysis, north Korea's move in addition to the United States and South Korea, but also intended to restrict the Japanese government. On the other hand, the South Korean military analyzed that the North may attempt to stir up a division of public opinion in the South by launching missiles as a show of force to protest the deployment of thaAD. config:config];
    self.YYGView.data=data;
    self.YYGView.height=data.height;
    self.YYGView.backgroundColor=[UIColor lightGrayColor];Copy the code