如何绘制非矩形UITextView?

时间:2012-02-14 02:43:00

标签: ios objective-c uitextview

我只想创建一个像这样的UITextView(不是两个textviews,空白区域是一个uiimage)

enter image description here

2 个答案:

答案 0 :(得分:15)

没有内置的UIView子类可以执行此操作(如果您编写正确的HTML和CSS,则除了UIWebView),但使用Core Text非常容易。我已将我的测试项目放在my ShapedLabel github repository中,这就是它的样子:

ShapedLabel screen shot

该项目有一个名为UIView的{​​{1}}子类。这是它的工作原理。

创建一个名为ShapedLabel的{​​{1}}子类。给它这些属性:

UIView

您需要覆盖每个属性setter方法以发送ShapedLabel,例如:

@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;

我依赖ARC来担心释放setNeedsDisplay的旧值。如果您不使用ARC ...开始。它非常简单,自iOS 4.0以来一直受到支持。

无论如何,那么你需要实现- (void)setFontName:(NSString *)fontName { _fontName = [fontName copy]; [self setNeedsDisplay]; } ,真正完成工作。首先,如果已设置,我们将使用_fontName填写形状:

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); CTFont不同。您可以使用CGFontUIFont转换为CGFont,但无法轻松将CTFont转换为CTFontCreateWithGraphicsFont。无论如何,我们只是直接创建UIFont

CTFont

我们创建属性字典,定义我们想要查看的所有样式属性:

CTFont

一旦我们有了属性字典,我们就可以创建将属性字典附加到文本字符串的属性字符串。这是Core Text使用的:

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

我们创建一个Core Text framesetter,它将从属性字符串中布置文本:

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

核心文本假定图形上下文将具有“标准”核心图形坐标系,其原点位于左下角。但是UIKit改变了上下文以将原点放在左上角。我们假设路径是在考虑到这一点的情况下创建的。所以我们需要一个垂直翻转坐标系的变换:

    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);

最后我们绘制文字。我们需要再次

path

这就是它。你现在可以在屏幕上放置一个漂亮的标签。

对于后代(如果我删除了测试项目),这里是 CTFrameRef frame = CTFramesetterCreateFrame(setter, CFRangeMake(0, 0), flippedPath, NULL); CFRelease(flippedPath); CFRelease(setter); 类的完整来源。

ShapedLabel.h

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

ShapedLabel.m

ShapedLabel

答案 1 :(得分:0)

根据Dannie P的建议here,使用textView.textContainer.exclusionPaths

Swift中的示例:

class WrappingTextVC: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()

    let textView = UITextView()
    textView.translatesAutoresizingMaskIntoConstraints = false
    textView.text = "ropcap example. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris aliquam vulputate ex. Fusce interdum ultricies justo in tempus. Sed ornare justo in purus dignissim, et rutrum diam pulvinar. Quisque tristique eros ligula, at dictum odio tempor sed. Fusce non nisi sapien. Donec libero orci, finibus ac libero ac, tristique pretium ex. Aenean eu lorem ut nulla elementum imperdiet. Ut posuere, nulla ut tincidunt viverra, diam massa tincidunt arcu, in lobortis erat ex sed quam. Mauris lobortis libero magna, suscipit luctus lacus imperdiet eu. Ut non dignissim lacus. Vivamus eget odio massa. Aenean pretium eget erat sed ornare. In quis tortor urna. Quisque euismod, augue vel pretium suscipit, magna diam consequat urna, id aliquet est ligula id eros. Duis eget tristique orci, quis porta turpis. Donec commodo ullamcorper purus. Suspendisse et hendrerit mi. Nulla pellentesque semper nibh vitae vulputate. Pellentesque quis volutpat velit, ut bibendum magna. Morbi sagittis, erat rutrum  Suspendisse potenti. Nulla facilisi. Praesent libero est, tincidunt sit amet tempus id, blandit sit amet mi. Morbi sed odio nunc. Mauris lobortis elementum orci, at consectetur nisl egestas a. Pellentesque vel lectus maximus, semper lorem eget, accumsan mi. Etiam semper tellus ac leo porta lobortis."
    textView.backgroundColor = .lightGray
    textView.textColor = .black
    view.addSubview(textView)

    textView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20).isActive = true
    textView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20).isActive = true
    textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
    textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -40).isActive = true

    let imageView = UIImageView(image: UIImage(named: "so-icon"))
    imageView.backgroundColor = .lightText
    imageView.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
    imageView.sizeToFit()
    textView.addSubview(imageView)

    textView.textContainer.exclusionPaths = [UIBezierPath(rect: imageView.frame)]
  }
}

结果:

Text wraps around image

Full example on github