iOS 7 TextKit - 如何在文本中插入图片?

111
我正在尝试使用UITextView实现以下效果: enter image description here 基本上,我想在文本之间插入一张图片。该图片只需占用1行空间,因此无需换行。
我尝试将UIView添加到子视图中:
UIView *pictureView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 25, 25)];
[pictureView setBackgroundColor:[UIColor redColor]];
[self.textView addSubview:pictureView];
但它似乎漂浮在文字上覆盖了它。
我对排除路径进行了一些阅读,这似乎是实现此功能的一种方式。但是,我不想绝对定位图片 - 相反,它应该与文本一起流动(类似于HTML中< span>的行为)。

一些答案提到了在NSTextAttachment和NSTextField上使用图像属性,但我想提到我需要一种允许我附加UIView的解决方案。 - Andy Hin
7
今天早上我在WWE网络上观看了2011年皇家大战比赛(你的形象就是从那里抓取的),真是太神奇了,今天我又偶然遇到了这个问题。 - bpapa
嗨,你有关于TextAttachment的一些可行代码示例吗? - fatuhoku
5个回答

183
你需要使用一个属性字符串,并将图片作为NSTextAttachment实例进行添加:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"like after"];

NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
textAttachment.image = [UIImage imageNamed:@"whatever.png"];

NSAttributedString *attrStringWithImage = [NSAttributedString attributedStringWithAttachment:textAttachment];

[attributedString replaceCharactersInRange:NSMakeRange(4, 1) withAttributedString:attrStringWithImage];

4
迄今为止,我认为这是最接近的答案。是否可以使用相同的技术来处理UIView而不是UIImage? - Andy Hin
4
在使用UITextView中的结果字符串时,图片无法显示。 - Deepak Khiwani
7
我该如何调整NSTextAttachment的大小? - jsetting32
2
@bilobatum,我想在TextView中添加多张图片。那么我该怎么做呢? - Diken Shah
3
[attributedString insertAttributedString:textAttachment atIndex:4]replaceCharactersInRange更好。 - DawnSong
显示剩余4条评论

30

@bilobatum的代码已转换为Swift,供需要的人使用:

let attributedString = NSMutableAttributedString(string: "like after")

let textAttachment = NSTextAttachment()

textAttachment.image = UIImage(named: "whatever.png")

let attrStringWithImage = NSAttributedString(attachment: textAttachment)

attributedString.replaceCharacters(in: NSMakeRange(4, 1), with: attrStringWithImage)

21

我认为这是目前为止最接近的答案。是否可以使用与UIImage相同的技术来处理UIView? - Andy Hin
你可能需要对自定义类进行一些重大的工作。NSTextAttachment具有默认图像属性,并且附件存储为NSAttributedString的一部分。你可能可以创建自己的子类并存储任何你想要的东西。我认为显示仅限于显示图像,因此不确定UIView是否有用。据我回忆,layoutManager期望一个图像。 - Duncan Groenewald
1
@AndyHin 我自己没有测试过,但可能的一个选项是将你的 UIView 渲染成一个 UIImage,然后将其作为 NSTextAttachment 添加。为了将视图渲染成图像,请查看这个问题:https://dev59.com/s2855IYBdhLWcg3wcz_y?lq=1 - Michael Gaylord

6

一个简单例子中的问题解决方案如下:

输入图像说明
let attachment = NSTextAttachment()
attachment.image = UIImage(named: "qrcode")

let firstString = NSMutableAttributedString(string: "scan the ")
let iconString = NSAttributedString(attachment: attachment)
let secondString = NSAttributedString(string: "QR code received on your phone.")

firstString.append(iconString)
firstString.append(secondString)
    
self.textLabel.attributedText = firstString

5

在@bilobatum的答案基础上,结合另一个问题中的这个类别,我做出了以下修改:

用法:

UILabel *labelWithImage = [UILabel new];
labelWithImage.text = @"Tap [new-button] to make a new thing!";
NSAttributedString *stringWithImage = [labelWithImage.attributedText attributedStringByReplacingOccurancesOfString:@"[new-button]" withImage:[UIImage imageNamed:@"MyNewThingButtonImage"] scale:0];
labelWithImage.attributedText = stringWithImage;

实施:

@interface NSMutableAttributedString (InlineImage)

- (void)replaceCharactersInRange:(NSRange)range withInlineImage:(UIImage *)inlineImage scale:(CGFloat)inlineImageScale;

@end

@interface NSAttributedString (InlineImages)

- (NSAttributedString *)attributedStringByReplacingOccurancesOfString:(NSString *)string withInlineImage:(UIImage *)inlineImage scale:(CGFloat)inlineImageScale;

@end

.

@implementation NSMutableAttributedString (InlineImages)

- (void)replaceCharactersInRange:(NSRange)range withInlineImage:(UIImage *)inlineImage scale:(CGFloat)inlineImageScale {

    if (floorf(inlineImageScale) == 0)
        inlineImageScale = 1.0f;

    // Create resized, tinted image matching font size and (text) color
    UIImage *imageMatchingFont = [inlineImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
    {
        // Font size
        NSDictionary *attributesForRange = [self attributesAtIndex:range.location effectiveRange:nil];
        UIFont *fontForRange = [attributesForRange valueForKey:NSFontAttributeName];
        CGSize imageSizeMatchingFontSize = CGSizeMake(inlineImage.size.width * (fontForRange.capHeight / inlineImage.size.height), fontForRange.capHeight);

        // Some scaling for prettiness
        CGFloat defaultScale = 1.4f;
        imageSizeMatchingFontSize = CGSizeMake(imageSizeMatchingFontSize.width * defaultScale,     imageSizeMatchingFontSize.height * defaultScale);
        imageSizeMatchingFontSize = CGSizeMake(imageSizeMatchingFontSize.width * inlineImageScale, imageSizeMatchingFontSize.height * inlineImageScale);
        imageSizeMatchingFontSize = CGSizeMake(ceilf(imageSizeMatchingFontSize.width), ceilf(imageSizeMatchingFontSize.height));

        // Text color
        UIColor *textColorForRange = [attributesForRange valueForKey:NSForegroundColorAttributeName];

        // Make the matching image
        UIGraphicsBeginImageContextWithOptions(imageSizeMatchingFontSize, NO, 0.0f);
        [textColorForRange set];
        [inlineImage drawInRect:CGRectMake(0 , 0, imageSizeMatchingFontSize.width, imageSizeMatchingFontSize.height)];
        imageMatchingFont = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }

    // Text attachment with image
    NSTextAttachment *textAttachment = [NSTextAttachment new];
    textAttachment.image = imageMatchingFont;
    NSAttributedString *imageString = [NSAttributedString attributedStringWithAttachment:textAttachment];

    [self replaceCharactersInRange:range withAttributedString:imageString];
}

@end

@implementation NSAttributedString (InlineImages)

- (NSAttributedString *)attributedStringByReplacingOccurancesOfString:(NSString *)string withInlineImage:(UIImage *)inlineImage scale:(CGFloat)inlineImageScale {

    NSMutableAttributedString *attributedStringWithImages = [self mutableCopy];

    [attributedStringWithImages.string enumerateOccurancesOfString:string usingBlock:^(NSRange substringRange, BOOL *stop) {
        [attributedStringWithImages replaceCharactersInRange:substringRange withInlineImage:inlineImage scale:inlineImageScale];

    }];

    return [attributedStringWithImages copy];
}

@end

将以下代码行进行更改: UIFont *fontForRange = [attributesForRange valueForKey:NSFontAttributeName]; 改为 UIFont *fontForRange = [attributesForRange valueForKey:NSFontAttributeName] ?: [UIFont fontWithName:@"HelveticaNeue" size:12.0]; 这样做会更好,因为如果在字典中没有设置[attributesForRange valueForKey:NSFontAttributeName],它可能为空。 - Victor Kwok

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接