Open full image in conversation controller with Atlas (Layer Chat)

Atlas is a fully featured, high performance, 100% customizable UI kit, built by Layer to power communications interfaces in any app. If you want to learn how to integrate chat in your iOS app, take a look at Implementing Chat/Messaging in iOS apps with Layer.

By default, Atlas doesn’t open full resolution images when a message with an image is clicked. This tutorial explains how to configure Atlas to show the full image when on message click.

Atlas provides a conversationViewController:didSelectMessage method which the class implementing ATLConversationViewControllerDelegate can implement to perform actions when a message is clicked. We will check whether the message contains an image and present an image view controller if it does.

- (void)conversationViewController:(ATLConversationViewController *)viewController didSelectMessage:(LYRMessage *)message {
    LYRMessagePart *JPEGMessagePart = ATLMessagePartForMIMEType(message, ATLMIMETypeImageJPEG);
    if (JPEGMessagePart) {
        [self presentImageViewControllerWithMessage:message];
        return;
    }
    LYRMessagePart *PNGMessagePart = ATLMessagePartForMIMEType(message, ATLMIMETypeImagePNG);
    if (PNGMessagePart) {
        [self presentImageViewControllerWithMessage:message];
    }
}

In order to show the image, we first need to load the image from Layer. Create a method loadLowResImageForMessage: that loads the low resolution image for a message.

- (UIImage *)loadLowResImageForMessage:(LYRMessage *)message {
    LYRMessagePart *lowResImagePart = ATLMessagePartForMIMEType(message, ATLMIMETypeImageJPEGPreview);
    LYRMessagePart *imageInfoPart = ATLMessagePartForMIMEType(message, ATLMIMETypeImageSize);

    if (!lowResImagePart) {
        // Default back to image/jpeg MIMEType
        lowResImagePart = ATLMessagePartForMIMEType(message, ATLMIMETypeImageJPEG);
    }

    // Retrieve low-res image from message part
    if (!(lowResImagePart.transferStatus == LYRContentTransferReadyForDownload || lowResImagePart.transferStatus == LYRContentTransferDownloading)) {
        if (lowResImagePart.fileURL) {
            return [UIImage imageWithContentsOfFile:lowResImagePart.fileURL.path];
        } else {
            return [UIImage imageWithData:lowResImagePart.data];
        }
    }
    return nil;
}

To present the image in a new view controller, we will use the JTSImageViewController library to simplify our work. Include the following in your Podfile and do pod install.

pod 'JTSImageViewController', '~> 1.4'

If you are using some other library, modify the following method accordingly.

- (void)presentImageViewControllerWithMessage:(LYRMessage *)message {
    // Create image info
    JTSImageInfo *imageInfo = [[JTSImageInfo alloc] init];
    imageInfo.image = [self loadLowResImageForMessage:message];

    // Setup view controller
    JTSImageViewController *imageViewer = [[JTSImageViewController alloc] initWithImageInfo:imageInfo mode:JTSImageViewControllerMode_Image backgroundStyle:JTSImageViewControllerBackgroundOption_Scaled message:message];

    // Present the view controller.
    [imageViewer showFromViewController:self transition:JTSImageViewControllerTransition_FromOffscreen];
}

However, this just loads the low resolution image in the view controller. Let’s initialize the view controller with the low res image and load the high resolution image in the background and replace it when it’s available. Create a subclass of JTSImageViewController named PGConversationImageViewController and use that in the above method instead of JTSImageViewController.

//
//  PGConversationImageViewController.h
//

#import "JTSImageViewController.h"

@interface PGConversationImageViewController : JTSImageViewController <LYRProgressDelegate>
@property(nonatomic, strong) LYRMessage *message;

- (id)initWithImageInfo:(JTSImageInfo *)info mode:(enum JTSImageViewControllerMode)mode backgroundStyle:(enum JTSImageViewControllerBackgroundOptions)style message:(LYRMessage *)message;
@end


//
//  PGConversationImageViewController.m
//

#import <LayerKit/LYRMessagePart.h>
#import <Atlas/Utilities/ATLMessagingUtilities.h>
#import "PGConversationImageViewController.h"

@interface PGConversationImageViewController ()
@property(nonatomic, strong) UIImage *fullResImage;
- (void)updateInterfaceWithImage:(UIImage *)image;
@end

@implementation PGConversationImageViewController
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self downloadFullResImageIfNeeded];
}

- (id)initWithImageInfo:(JTSImageInfo *)info mode:(enum JTSImageViewControllerMode)mode backgroundStyle:(enum JTSImageViewControllerBackgroundOptions)style message:(LYRMessage *)message {
    PGConversationImageViewController *_self = [self initWithImageInfo:self.imageInfo mode:mode backgroundStyle:style];
    if (_self) {
        _self.message = message;
    }
    return _self;
}

#pragma mark - Layer Image Downloads

- (void)loadFullResImages {
    LYRMessagePart *fullResImagePart = ATLMessagePartForMIMEType(self.message, ATLMIMETypeImageJPEG);
    if (!fullResImagePart) {
        fullResImagePart = ATLMessagePartForMIMEType(self.message, ATLMIMETypeImagePNG);
    }

    // Retrieve hi-res image from message part
    if (!(fullResImagePart.transferStatus == LYRContentTransferReadyForDownload || fullResImagePart.transferStatus == LYRContentTransferDownloading)) {
        if (fullResImagePart.fileURL) {
            self.fullResImage = [UIImage imageWithContentsOfFile:fullResImagePart.fileURL.path];
        } else {
            self.fullResImage = [UIImage imageWithData:fullResImagePart.data];
        }
        [self updateInterfaceWithImage:self.fullResImage];
    }
}

- (void)downloadFullResImageIfNeeded {
    LYRMessagePart *fullResImagePart = ATLMessagePartForMIMEType(self.message, ATLMIMETypeImageJPEG);
    if (!fullResImagePart) {
        fullResImagePart = ATLMessagePartForMIMEType(self.message, ATLMIMETypeImagePNG);
    }

    // Download hi-res image from the network
    if (fullResImagePart && (fullResImagePart.transferStatus == LYRContentTransferReadyForDownload || fullResImagePart.transferStatus == LYRContentTransferDownloading)) {
        NSError *error;
        LYRProgress *downloadProgress = [fullResImagePart downloadContent:&error];
        if (!downloadProgress) {
            NSLog(@"Problem downloading full resolution photo - %@", error);
            return;
        }
        downloadProgress.delegate = self;
    } else {
        [self loadFullResImages];
    }
}

#pragma mark - LYRProgress Delegate Implementation

- (void)progressDidChange:(LYRProgress *)progress {
    // Queue UI updates onto the main thread, since LYRProgress performs
    // delegate callbacks from a background thread.
    dispatch_async(dispatch_get_main_queue(), ^{
        BOOL progressCompleted = progress.fractionCompleted == 1.0f;
        // After transfer completes, remove self for delegation.
        if (progressCompleted) {
            progress.delegate = nil;
            self.title = @"Image Downloaded";
            [self loadFullResImages];
        }
    });
}

@end

Here’s how it should look now:

LayerChatImage byvyup

Need help setting this up or just want to chat? Shoot me an email at pulkit110@gmail.com.

Published 13 May 2015

I build mobile and web applications. Full Stack, Rails, React, Typescript, Kotlin, Swift
Pulkit Goyal on Twitter