iOS 7 TextKit - ¿Cómo insertar imágenes en línea con texto?

109

Estoy tratando de obtener el siguiente efecto usando un UITextView:

ingrese la descripción de la imagen aquí

Básicamente quiero insertar una imagen entre el texto. La imagen simplemente puede ocupar 1 fila de espacio, por lo que no es necesario envolver.

Intenté agregar un UIView a la subvista:

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

Pero parece flotar sobre el texto y cubrirlo.

Leí un poco sobre las rutas de exclusión, que parece ser una forma de implementar esto. Sin embargo, no quiero posicionar absolutamente la imagen; en su lugar, debería fluir con el texto (similar a cómo se <span>comporta en HTML).

Andy Hin
fuente
Algunas de las respuestas mencionan el uso de las propiedades de la imagen en NSTextAttachment y NSTextField, pero quiero mencionar que necesito una solución que me permita agregar un UIView.
Andy Hin
5
Es increíble que acabo de ver el Royal Rumble 2011 esta mañana (de donde se tomó su imagen) a través de la red WWE y aquí estoy hablando de esta pregunta hoy.
bpapa
Oye, ¿tienes un ejemplo de algún código de trabajo que involucre TextAttachment?
fatuhoku

Respuestas:

180

Deberá usar una cadena con atributos y agregar la imagen como instancia 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
fuente
4
Creo que esta es la respuesta más cercana hasta ahora. ¿Es posible utilizar esta misma técnica con UIView en lugar de UIImage?
Andy Hin
4
Esto no muestra las imágenes cuando se usa la cadena resultante en UITextView
DeepK SOreadytohelp
6
¿Cómo cambio el tamaño del NSTextAttachment?
jsetting32
2
@bilobatum, quiero agregar más de una imagen a la vista de texto. Entonces, ¿cómo puedo agregar?
Diken Shah
3
`[attributeString insertAttributedString: textAttachment atIndex: 4]` es mejor quereplaceCharactersInRange
DawnSong
26

Código de @ bilobatum convertido a Swift para aquellos que lo necesitan:

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
fuente
20

Puede intentar usar NSAttributedString y NSTextAttachment. Eche un vistazo al siguiente enlace para obtener más detalles sobre cómo personalizar NSTextAttachment para cambiar el tamaño de la imagen. http://ossh.com.au/design-and-technology/software-development/implementing-rich-text-with-images-on-os-x-and-ios/

En mi ejemplo, cambio el tamaño de la imagen para que se ajuste al ancho, en su caso, es posible que desee cambiar el tamaño de la imagen para que coincida con la altura de la línea.

Duncan Groenewald
fuente
Creo que esta es la respuesta más cercana hasta ahora. ¿Es posible utilizar esta misma técnica con UIView en lugar de UIImage?
Andy Hin
Es posible que pueda realizar un trabajo importante en sus propias clases personalizadas. NSTextAttachment tiene un atributo de imagen predeterminado y el archivo adjunto se almacena como parte de NSAttributedString. Probablemente podría crear sus propias subclases y almacenar lo que quiera. Creo que la visualización se limita a mostrar una imagen, por lo que no estoy seguro de que una UIView sea útil. Según recuerdo, layoutManager espera una imagen.
Duncan Groenewald
1
@AndyHin No lo he probado yo mismo, pero una opción es posiblemente convertirlo en UIViewa UIImagey luego agregarlo como NSTextAttachment. Para representar la vista en una imagen, consulte esta pregunta: http://stackoverflow.com/questions/4334233/how-to-capture-uiview-to-uiimage-without-loss-of-quality-on-retina- display? lq = 1
Michael Gaylord
¿Algún nuevo desarrollo con esto?
fatuhoku
5

Ampliando la respuesta de @ bilobatum y usando esta categoría de otra pregunta. Cociné esto:

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;

Implementación:

@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
fuente
Cambiar la línea UIFont *fontForRange = [attributesForRange valueForKey:NSFontAttributeName];a UIFont *fontForRange = [attributesForRange valueForKey:NSFontAttributeName] ?: [UIFont fontWithName:@"HelveticaNeue" size:12.0];debería ser mejor, porque [attributeForRange valueForKey: NSFontAttributeName] podría ser nulo si no se configuró en el diccionario
Victor Kwok
5

La solución del problema en un ejemplo simple es ingrese la descripción de la imagen aquí

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
fuente