iOS 7 TextKit - Comment insérer des images en ligne avec du texte?

109

J'essaie d'obtenir l'effet suivant en utilisant un UITextView:

entrez la description de l'image ici

Fondamentalement, je veux insérer une image entre le texte. L'image peut simplement occuper 1 rangée d'espace, donc aucun emballage n'est nécessaire.

J'ai simplement essayé d'ajouter un UIView à la sous-vue:

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

Mais il semble flotter sur le texte et le recouvrir.

J'ai fait un peu de lecture sur les chemins d'exclusion qui semblent être une façon de mettre en œuvre cela. Cependant, je ne veux pas positionner absolument l'image - au lieu de cela, elle devrait suivre le texte (similaire à la façon dont <span>se comporte en HTML).

Andy Hin
la source
Quelques-unes des réponses mentionnent l'utilisation des propriétés d'image sur NSTextAttachment et NSTextField, mais je tiens à mentionner que j'ai besoin d'une solution qui me permet d'ajouter un UIView.
Andy Hin
5
Incroyable que je viens de regarder le Royal Rumble 2011 ce matin (d'où votre image a été capturée) via le réseau de la WWE et ici je passe sur cette question aujourd'hui.
bpapa
Heya, avez-vous un exemple de code de travail impliquant TextAttachment?
fatuhoku

Réponses:

180

Vous devrez utiliser une chaîne attribuée et ajouter l'image comme instance 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
la source
4
Je pense que c'est la réponse la plus proche à ce jour. Est-il possible d'utiliser cette même technique avec l'UIView au lieu de UIImage?
Andy Hin
4
Cela n'affiche pas les images lors de l'utilisation de la chaîne résultante dans UITextView
DeepK SOreadytohelp
6
Comment redimensionner le NSTextAttachment?
jsetting32
2
@bilobatum, je souhaite ajouter plus d'une image dans textview. Alors, comment puis-je ajouter?
Diken Shah
3
`[AttributeString insertAttributedString: textAttachment atIndex: 4]` est mieux quereplaceCharactersInRange
DawnSong
26

Code de @ bilobatum converti en Swift pour ceux qui en ont besoin:

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
la source
20

Vous pouvez essayer d'utiliser NSAttributedString et NSTextAttachment. Jetez un œil au lien suivant pour plus de détails sur la personnalisation de NSTextAttachment afin de redimensionner l'image. http://ossh.com.au/design-and-technology/software-development/implementing-rich-text-with-images-on-os-x-and-ios/

Dans mon exemple, je redimensionne l'image pour l'adapter à la largeur, dans votre cas, vous voudrez peut-être redimensionner l'image pour qu'elle corresponde à la hauteur de la ligne.

Duncan Groenewald
la source
Je pense que c'est la réponse la plus proche à ce jour. Est-il possible d'utiliser cette même technique avec l'UIView au lieu de UIImage?
Andy Hin
Vous pourrez peut-être effectuer des travaux majeurs sur vos propres classes personnalisées. NSTextAttachment a un attribut d'image par défaut et la pièce jointe est stockée dans le cadre de NSAttributedString. Vous pourriez probablement créer vos propres sous-classes et stocker ce que vous voulez. Je pense que l'affichage est limité à l'affichage d'une image, donc pas sûr qu'un UIView serait utile. Si je me souviens bien, le layoutManager attend une image.
Duncan Groenewald
1
@AndyHin Je n'ai pas testé cela moi-même, mais une option est peut-être de rendre votre fichier UIViewen a UIImage, puis de l'ajouter en tant que fichier NSTextAttachment. Pour rendre la vue à une image, consultez cette question: http://stackoverflow.com/questions/4334233/how-to-capture-uiview-to-uiimage-without-loss-of-quality-on-retina- afficher? lq = 1
Michael Gaylord
De nouveaux développements avec cela?
fatuhoku
5

Développement de la réponse de @ bilobatum et utilisation de cette catégorie à partir d'une autre question. J'ai concocté ceci:

Usage:

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;

La mise en oeuvre:

@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
la source
Changer la ligne UIFont *fontForRange = [attributesForRange valueForKey:NSFontAttributeName];en UIFont *fontForRange = [attributesForRange valueForKey:NSFontAttributeName] ?: [UIFont fontWithName:@"HelveticaNeue" size:12.0];devrait être mieux, car [attributesForRange valueForKey: NSFontAttributeName] pourrait être nul s'il n'était pas défini dans le dictionnaire
Victor Kwok
5

La solution du problème dans un exemple simple est entrez la description de l'image ici

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
la source