iOS 7 TextKit - Como inserir imagens alinhadas com o texto?

109

Estou tentando obter o seguinte efeito usando um UITextView:

insira a descrição da imagem aqui

Basicamente, quero inserir uma imagem entre o texto. A imagem pode simplesmente ocupar 1 linha de espaço, então não há necessidade de empacotamento.

Tentei apenas adicionar um UIView à subvisualização:

UIView *pictureView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 25, 25)];
[pictureView setBackgroundColor:[UIColor redColor]];
[self.textView addSubview:pictureView];

Mas parece flutuar sobre o texto e cobri-lo.

Eu li um pouco sobre caminhos de exclusão, o que parece ser uma forma de implementar isso. No entanto, não quero posicionar absolutamente a imagem - em vez disso, ela deve fluir com o texto (semelhante ao <span>comportamento em HTML).

Andy Hin
fonte
Algumas das respostas mencionam o uso das propriedades de imagem em NSTextAttachment e NSTextField, mas quero mencionar que preciso de uma solução que me permita anexar um UIView.
Andy Hin
5
Incrível que acabei de assistir ao Royal Rumble 2011 esta manhã (de onde sua imagem foi obtida) através da rede WWE e aqui estou falando sobre esta questão hoje.
bpapa
Ei, você tem um exemplo de algum código funcional envolvendo TextAttachment?
fatuhoku

Respostas:

180

Você precisará usar uma string atribuída e adicionar a imagem como instância de 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];
bilobatum
fonte
4
Acho que esta é a resposta mais próxima até agora. É possível usar essa mesma técnica com o UIView em vez de UIImage?
Andy Hin
4
Isso não está exibindo as imagens ao usar a string resultante em UITextView
DeepK SOreadytohelp
6
Como faço para redimensionar o NSTextAttachment?
jsetting32
2
@bilobatum, quero adicionar mais de uma imagem ao textview. Então, como posso adicionar?
Diken Shah
3
`[attributeString insertAttributedString: textAttachment atIndex: 4]` é melhor do quereplaceCharactersInRange
DawnSong
26

Código de @bilobatum convertido em Swift para quem precisa:

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)
Justin Vallely
fonte
20

Você pode tentar usar NSAttributedString e NSTextAttachment. Dê uma olhada no link a seguir para obter mais detalhes sobre como personalizar o NSTextAttachment para redimensionar a imagem. http://ossh.com.au/design-and-technology/software-development/implementing-rich-text-with-images-on-os-x-and-ios/

No meu exemplo, eu redimensiono a imagem para caber na largura; no seu caso, você pode redimensionar a imagem para corresponder à altura da linha.

Duncan Groenewald
fonte
Acho que esta é a resposta mais próxima até agora. É possível usar essa mesma técnica com o UIView em vez de UIImage?
Andy Hin
Você pode conseguir com algum trabalho importante em suas próprias classes personalizadas. NSTextAttachment tem um atributo de imagem padrão e o anexo é armazenado como parte de NSAttributedString. Você provavelmente pode criar suas próprias subclasses e armazenar o que quiser. Acho que a exibição se limita a exibir uma imagem, então não tenho certeza se um UIView seria útil. Pelo que me lembro, o layoutManager espera uma imagem.
Duncan Groenewald
1
@AndyHin Eu não testei isso, mas uma opção é possivelmente renderizar seu UIViewem a UIImagee adicioná-lo como um NSTextAttachment. Para renderizar a visualização de uma imagem, verifique esta pergunta: http://stackoverflow.com/questions/4334233/how-to-capture-uiview-to-uiimage-without-loss-of-quality-on-retina- display? lq = 1
Michael Gaylord
Algum novo desenvolvimento com isso?
fatuhoku
5

Expandindo a resposta de @ bilobatum e usando esta categoria de outra pergunta. Eu cozinhei isso:

Uso:

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;

Implementação:

@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
Stian Høiland
fonte
Alterar a linha UIFont *fontForRange = [attributesForRange valueForKey:NSFontAttributeName];para UIFont *fontForRange = [attributesForRange valueForKey:NSFontAttributeName] ?: [UIFont fontWithName:@"HelveticaNeue" size:12.0];deve ser melhor, porque [attributeForRange valueForKey: NSFontAttributeName] pode ser nulo se não estiver definido no dicionário
Victor Kwok
5

A solução do problema em um exemplo simples é insira a descrição da imagem aqui

let attachment = NSTextAttachment()
attachment.image = UIImage(named: "qrcode")

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

firstString.append(iconString)
firstString.append(secondString)

self.textLabel.attributedText = firstString
Prabhat Kasera
fonte