如何绘制一个非矩形的uitextview?

我只是想创build一个像这样的UITextview(不是两个textview,空白区域是一个uiimage :)),它可以自动input新行,如何?

在这里输入图像说明

没有内置的UIView子类可以做到这一点(如果你编写正确的HTML和CSS, UIWebView除外),但使用Core Text很容易。 我把我的testing项目放在我的ShapedLabel github仓库中 ,看起来像这样:

ShapedLabel屏幕截图

该项目有一个名为ShapedLabelUIView子类。 这是如何工作的。

创build一个名为ShapedLabelUIView子类。 给它这些属性:

 @property (nonatomic, copy) NSString *text; @property (nonatomic) UITextAlignment textAlignment; @property (nonatomic, copy) NSString *fontName; @property (nonatomic) CGFloat fontSize; @property (nonatomic, strong) UIColor *textColor; @property (nonatomic, strong) UIColor *shapeColor; @property (nonatomic, copy) UIBezierPath *path; 

你会想要覆盖每个属性setter方法来发送setNeedsDisplay ,就像这样:

 - (void)setFontName:(NSString *)fontName { _fontName = [fontName copy]; [self setNeedsDisplay]; } 

我依靠ARC来担心释放_fontName的旧值。 如果你不使用ARC …开始。 从iOS 4.0开始,它变得非常容易并且受到支持。

无论如何,那么你需要实现drawRect:真正的工作完成。 首先,如果已经设置,我们将使用shapeColor填充形状:

 - (void)drawRect:(CGRect)rect { if (!_path) return; if (_shapeColor) { [_shapeColor setFill]; [_path fill]; } 

我们检查以确保我们有我们需要的所有其他参数:

  if (!_text || !_textColor || !_fontName || _fontSize <= 0) return; 

接下来我们处理textAligment属性:

  CTTextAlignment textAlignment = NO ? 0 : _textAlignment == UITextAlignmentCenter ? kCTCenterTextAlignment : _textAlignment == UITextAlignmentRight ? kCTRightTextAlignment : kCTLeftTextAlignment; CTParagraphStyleSetting paragraphStyleSettings[] = { { .spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof textAlignment, .value = &textAlignment } }; CTParagraphStyleRef style = CTParagraphStyleCreate(paragraphStyleSettings, sizeof paragraphStyleSettings / sizeof *paragraphStyleSettings); 

我们接下来创buildCTFont 。 请注意,这与CGFontUIFont 。 您可以使用CTFontCreateWithGraphicsFontCGFont转换为CGFont ,但不能轻松将UIFont转换为CTFont 。 无论如何,我们只是直接创buildCTFont

  CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)_fontName, _fontSize, NULL); 

我们创build属性字典来定义我们想要看到的所有样式属性:

  NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)font, kCTFontAttributeName, _textColor.CGColor, kCTForegroundColorAttributeName, style, kCTParagraphStyleAttributeName, nil]; CFRelease(font); CFRelease(style); 

一旦我们有属性字典,我们可以创build属性string,将属性字典附加到文本string。 这是Core Text使用的内容:

  CFAttributedStringRef trib = CFAttributedStringCreate(NULL, (__bridge CFStringRef)_text, (__bridge CFDictionaryRef)attributes); 

我们创build了一个核心文本框架,它将从属性string中排列文本:

  CTFramesetterRef setter = CTFramesetterCreateWithAttributedString(trib); CFRelease(trib); 

核心文本假定graphics上下文将具有“标准”核心graphics坐标系,原点在左下angular。 但UIKit更改上下文以将原点置于左上方。 我们将假设这条路是为了这个而创build的。 所以我们需要一个垂直翻转坐标系的变换:

  // Core Text lays out text using the default Core Graphics coordinate system, with the origin at the lower left. We need to compensate for that, both when laying out the text and when drawing it. CGAffineTransform textMatrix = CGAffineTransformIdentity; textMatrix = CGAffineTransformTranslate(textMatrix, 0, self.bounds.size.height); textMatrix = CGAffineTransformScale(textMatrix, 1, -1); 

然后我们可以创build一个path的翻转副本:

  CGPathRef flippedPath = CGPathCreateCopyByTransformingPath(_path.CGPath, &textMatrix); 

最后我们可以要求制作者摆出一段文字。 这实际上符合由path属性定义的形状内的文本:

  CTFrameRef frame = CTFramesetterCreateFrame(setter, CFRangeMake(0, 0), flippedPath, NULL); CFRelease(flippedPath); CFRelease(setter); 

最后我们画出文字。 我们需要再次

  CGContextRef gc = UIGraphicsGetCurrentContext(); CGContextSaveGState(gc); { CGContextConcatCTM(gc, textMatrix); CTFrameDraw(frame, gc); } CGContextRestoreGState(gc); CFRelease(frame); } 

这是非常多的。 你现在可以在屏幕上放置一个不错的形状的标签。

对于后人(如果我删除了testing项目),这里是ShapedLabel类的完整源代码。

ShapedLabel.h

 #import <UIKit/UIKit.h> @interface ShapedLabel : UIView @property (nonatomic, copy) NSString *text; @property (nonatomic) UITextAlignment textAlignment; @property (nonatomic, copy) NSString *fontName; @property (nonatomic) CGFloat fontSize; @property (nonatomic, strong) UIColor *textColor; @property (nonatomic, strong) UIColor *shapeColor; @property (nonatomic, copy) UIBezierPath *path; @end 

ShapedLabel.m

 #import "ShapedLabel.h" #import <CoreText/CoreText.h> @implementation ShapedLabel @synthesize fontName = _fontName; @synthesize fontSize = _fontSize; @synthesize path = _path; @synthesize text = _text; @synthesize textColor = _textColor; @synthesize shapeColor = _shapeColor; @synthesize textAlignment = _textAlignment; - (void)commonInit { _text = @""; _fontSize = UIFont.systemFontSize; // There is no API for just getting the system font name, grr... UIFont *uiFont = [UIFont systemFontOfSize:_fontSize]; _fontName = [uiFont.fontName copy]; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self commonInit]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { [self commonInit]; } return self; } - (void)setFontName:(NSString *)fontName { _fontName = [fontName copy]; [self setNeedsDisplay]; } - (void)setFontSize:(CGFloat)fontSize { _fontSize = fontSize; [self setNeedsDisplay]; } - (void)setPath:(UIBezierPath *)path { _path = [path copy]; [self setNeedsDisplay]; } - (void)setText:(NSString *)text { _text = [text copy]; [self setNeedsDisplay]; } - (void)setTextColor:(UIColor *)textColor { _textColor = textColor; [self setNeedsDisplay]; } - (void)setTextAlignment:(UITextAlignment)textAlignment { _textAlignment = textAlignment; [self setNeedsDisplay]; } - (void)setShapeColor:(UIColor *)shapeColor { _shapeColor = shapeColor; [self setNeedsDisplay]; } - (void)drawRect:(CGRect)rect { if (!_path) return; if (_shapeColor) { [_shapeColor setFill]; [_path fill]; } if (!_text || !_textColor || !_fontName || _fontSize <= 0) return; CTTextAlignment textAlignment = NO ? 0 : _textAlignment == UITextAlignmentCenter ? kCTCenterTextAlignment : _textAlignment == UITextAlignmentRight ? kCTRightTextAlignment : kCTLeftTextAlignment; CTParagraphStyleSetting paragraphStyleSettings[] = { { .spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof textAlignment, .value = &textAlignment } }; CTParagraphStyleRef style = CTParagraphStyleCreate(paragraphStyleSettings, sizeof paragraphStyleSettings / sizeof *paragraphStyleSettings); CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)_fontName, _fontSize, NULL); NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)font, kCTFontAttributeName, _textColor.CGColor, kCTForegroundColorAttributeName, style, kCTParagraphStyleAttributeName, nil]; CFRelease(font); CFRelease(style); CFAttributedStringRef trib = CFAttributedStringCreate(NULL, (__bridge CFStringRef)_text, (__bridge CFDictionaryRef)attributes); CTFramesetterRef setter = CTFramesetterCreateWithAttributedString(trib); CFRelease(trib); // Core Text lays out text using the default Core Graphics coordinate system, with the origin at the lower left. We need to compensate for that, both when laying out the text and when drawing it. CGAffineTransform textMatrix = CGAffineTransformIdentity; textMatrix = CGAffineTransformTranslate(textMatrix, 0, self.bounds.size.height); textMatrix = CGAffineTransformScale(textMatrix, 1, -1); CGPathRef flippedPath = CGPathCreateCopyByTransformingPath(_path.CGPath, &textMatrix); CTFrameRef frame = CTFramesetterCreateFrame(setter, CFRangeMake(0, 0), flippedPath, NULL); CFRelease(flippedPath); CFRelease(setter); CGContextRef gc = UIGraphicsGetCurrentContext(); CGContextSaveGState(gc); { CGContextConcatCTM(gc, textMatrix); CTFrameDraw(frame, gc); } CGContextRestoreGState(gc); CFRelease(frame); } @end