How can I access the image from the MSMessage's layout on the receiving user's end? - ios10

I am making an iMessage Extension that involves users sending pictures back and forth to one another. They need to both be able to access the images that they receive from each other and use it on their own end. For example, if USER 1 sends a picture of a puppy to USER 2, the image property of the messages layout would be of the puppy. USER 2 should then be able to tap that message, and the puppy load into an image view on screen. So far I don't know how I would do this.
Here's where I set the layout image to that of a puppy.
#IBAction func sendPicturePressed(_ sender: AnyObject) {
if chosenImage.image != nil {
let session = MSSession()
let message = MSMessage(session: session)
let conversation = self.activeConversation
let components = URLComponents()
let layout = MSMessageTemplateLayout()
let image = chosenImage.image
layout.image = image
message.layout = layout
message.url = components.url!
conversation?.insert(message, completionHandler: { (error) in
self.dismiss()
})
}
}
Now when the second user taps the puppy, I want to set an image view on their screen to the puppy. Not exactly sure how, but what I'd LIKE to do is:
override func willBecomeActive(with conversation: MSConversation) {
imageView.image = conversation.selectedMessage.layout.image
//There is no image property to access this like I've provided, that's just what I'm trying to accomplish.
}

You can't access the layout image that send from another user.
https://forums.developer.apple.com/thread/53174
The selected Message having a nil layout is expected. Received
messages won't have a layout object filled in.
What you can, is on the sender side, you not only insert a MSMessage with image layout to MSConversation, but also upload an image copy to your server and mark the web image URL in MSMessage.URL.
So next time when user select a message, you can unwrap MSMessage.URL and find the image URL, then download it from your server.

Although you cannot access the layout once message is received, you still have once chance to get it. In case extension is launched, message's layout is accessible on arrival in -[didReceiveMessage:conversation:] method of your MSMessagesAppViewController-based class.
On the sender side you can assign your message a URL with custom ID (e.g. UUID) - it will be always accessible – then extract it on the receiver side in the didReceiveMessage together with the image and cache the latter locally with the ID as a name. Here's the code draft:
- (NSURL*) fileURLFromRawURL:(NSURL*)idURL
{
NSString *fileID = idURL.relativePath;
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:fileID];
return [NSURL fileURLWithPath:path];
}
- (MSMessage*) composeMessageWithImage:(UIImage*)image session:(MSSession*)session
{
MSMessageTemplateLayout *layout = [MSMessageTemplateLayout new];
layout.image = image;
MSMessage *message = [[MSMessage alloc] initWithSession:session];
message.layout = layout;
message.URL = [NSURL fileURLWithPath:[[NSUUID UUID] UUIDString]];
return message;
}
- (void) didReceiveMessage:(MSMessage *)message conversation:(MSConversation *)conversation
{
// Assuming that the extension is launched:
MSMessageTemplateLayout *layout = message.layout;
UIImage *img = layout.image; // you've got the image
NSURL *path = [self fileURLFromRawURL:message.URL];
if (path && image){
NSData *data = UIImageJPEGRepresentation((UIImage*)resource, 0.8);
[data writeToURL:path options:NSDataWritingAtomic error:nil];
}
}
After that, once message is tapped, the -[didSelectMessage:conversation:] will be called. Here you can get the URL again and read the corresponding image from cache (in case it is available):
- (void) didSelectMessage:(MSMessage *)message conversation:(MSConversation *)conversation
{
NSURL *path = [self fileURLFromRawURL:message.URL];
if (path){
NSData *imageData = [NSData dataWithContentsOfURL:path];
UIImage *img = [UIImage imageWithData:imageData];
}
}
And don't forget to add all necessary checks for nils, successfull URL creation etc.

Related

Multiple Threads Using GCD while Fetching Photos

I have to fetch contacts from the Address Book and show photo beside each if found in a UITableView.
I fetch all contacts using ABContactsHelper library and then asynchronously fetch photos for visible rows in the UITableView using GCD blocks.
I referred to an Apple Sample code which waits for the UITableView to finish scrolling, get Visible NSIndexPaths & created threads to fetch photos. My problem so far is two fold.
First, if user scrolls, stops, scrolls & stops and does it quite a few times, too many threads are generated for fetching photos which slows down the app.
Secondly, when the thread returns to set photo in cache as well as the UITableViewCell however, the reference to UIImageView is now being reused for another record in UITableViewCell, hence the photo is placed on wrong record which eventually gets replace by correct one, when thread for that particular record returns.
Here is the code I is used both in cellForRowAtIndexPath as well as when UITableView stops scrolling.
- (void)loadImagesLazilyForIndexPath:(NSIndexPath *)indexPath photo:(UIImageView *)photo contact:(ContactModel *)contact
{
if (!self.tableView.isDragging && !self.tableView.isDecelerating) {
UIImage *thePhoto = [self.imagesForContacts objectForKey:indexPath];
if (!thePhoto) {
// NSLog(#"Photo Not Found - Now Fetching %#", indexPath);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
#autoreleasepool {
UIImage *image = [[JMContactsManager sharedContactsManager] photoForContact:contact];
if (!image)
image = self.noProfilePhoto;
[self.imagesForContacts setObject:image forKey:indexPath];
dispatch_async(dispatch_get_main_queue(), ^{
// NSLog(#"Photo Fetched %#", indexPath);
#autoreleasepool {
NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
BOOL visible = [visiblePaths indexOfObjectPassingTest:^BOOL(NSIndexPath * ip, NSUInteger idx, BOOL *stop) {
if (ip.row == indexPath.row && ip.section == indexPath.section) {
*stop = YES;
return 1;
}
return 0;
}];
if (visible)
photo.image = [self.imagesForContacts objectForKey:indexPath];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
}
});
}
});
} else {
// NSLog(#"Photo Was Found %#", indexPath);
#autoreleasepool {
photo.image = [self.imagesForContacts objectForKey:indexPath];
}
}
}
}
For this kind of functionality I would go with an NSOperation and an NSOperationQueue, they are build on top of GCD, but it gives you the opportunity to cancel operations. You could check which operation aren't visible anymore and cancel them. In thi s way you can control reference "away".
I see also another issue that could lead into a "problem" it seems that you are caching images in an NSMutableDictionary, aren't you? Or are you using an NSCache? If it is an NScache is fine, but most of mutable object aren't thread safe "naturally"
Boost up the priority of the queue :-)
As mentioned by #Andrea, you should be using an NSOperationQueue, which gives you the ability to cancel queued tasks.
Indexing your image cache by indexPath into your table is not robust as an index path for a given element could change (although maybe not in your specific case). You might consider indexing your image cache by ABRecord.uniqueId instead.
In any case it will not solve the problem of your images being set twice or more for the same cell. This happens because UITableView does not assign a view for each item but manages a pool of UITableCellViews, which it re-uses each time. What you could do is something along the following lines:
// Assuming your "ContactCellView" inherits from UITableCellView and has a contact property
// declared as follows: #property (retain) ABRecord *contact.
- (void) setContact:(ABRecord*)contact
{
_contact = contact;
__block UIImage *thePhoto = [self.imagesForContacts objectForKey:contact.uniqueId];
if (thePhoto == nil) {
_loadImageOp = [NSBlockOperation blockOperationWithBlock:^(void) {
// Keep a local reference to the contact because it might change on us at any time.
ABRecord *fetchContact = contact;
// Fetch the photo as you normally would
thePhoto = [[JMContactsManager sharedContactsManager] photoForContact:fetchContact];
if (thePhoto == nil)
thePhoto = self.noProfilePhoto;
// Only assign the photo if the contact has not changed in the mean time.
if (fetchContact == _contact)
_contactPhotoView.image = thePhoto;
}];
} else {
_contactPhotoView.image = thePhoto;
}
}

Initial text and paperclipped-URL for action in UIActivityViewController & UIActivityItemSource?

Finally been making it through Apple's (rather dismal) documentation on the new UIActivityViewController class and the UIActivityItemSource protocol, and I'm trying to send different data sets to different actions called from the activity view. To simplify things, I'm looking at two things.
A Facebook posting action, which should say "Check this out!" and also attach a URL to the post (with that cute little paperclip).
A Twitter posting action, which should say "Check this out, with #hashtag!" and also attach that same URL (with the same paperclip).
Here's the code I've got implemented right now.
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType {
if ([activityType isEqualToString:UIActivityTypePostToFacebook]) {
return #"Check this out!";
} else if ([activityType isEqualToString:UIActivityTypePostToTwitter]) {
return #"Check this out, with #hashtag!";
}
return #"";
}
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController {
return #"";
}
And then when I set up this activity view controller (it's in the same class), this is what I do.
UIActivityViewController *activityView = [[UIActivityViewController alloc] initWithActivityItems:#[self] applicationActivities:nil];
[self presentViewController:activityView animated:YES completion:nil];
My dilemma is how to attach that NSURL object. It's relatively easy when calling the iOS 6 SL-class posting modals; you just call the individual methods to attach a URL or an image. How would I go about doing this here?
I'll note that instead of returning NSString objects from -activityViewController:itemForActivityType, if I return just NSURL objects, they show up with that paperclip, with no body text in the post. If I return an array of those two items, nothing shows up at all.
Evidently it was as simple as this: passing in an array to the first argument of UIActivityViewController's init call, with each item in the array handling a different data type that will end up in the compose screen. self handles the text, and the second object (the NSURL) attaches the URL.
NSArray *items = #[self, [NSURL URLWithString:#"http://this-is-a-url.com"]];
UIActivityViewController *activityView = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
[self presentViewController:activityView animated:YES completion:nil];
Really wish there was more on this, but here it is.

how to load few images from URL individually to scroll view ? objective-c

i have this scroll view, that I'm loading into him few images from URL.
the problem is that the scroll view don't show any of them until that all loaded.
i want to show every image the moment i finished loading her.
my code looks like this:
-(void)loadPhotosToLeftscroll{
for (int count = 0 ; count < [leftPhotoArray count]; count++) {
NSLog(#"nextPhotoHight: %f",nextLeftPhotoHight);
NSMutableDictionary *photoDict;
photoDict = [leftPhotoArray objectAtIndex:count];
float photoHight = [[photoDict objectForKey:#"photos_height"]floatValue];
float photoWide= [[photoDict objectForKey:#"photos_width"]floatValue];
NSString *photoPath = [photoDict objectForKey:#"photos_path"];
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:photoPath]];
UIImage *image = [[UIImage alloc] initWithData:imageData];
UIImageView *photoView = [[UIImageView alloc]initWithFrame:CGRectMake(10 , nextLeftPhotoHight, photoWide, photoHight )];
[photoView setImage:image];
[photoView.layer setMasksToBounds:YES];
[photoView.layer setCornerRadius:6];
nextLeftPhotoHight = photoView.frame.size.height + photoView.frame.origin.y + 10;
[leftBlockScroll addSubview:photoView];
}
}
You better use a asynchronous way to do that.
Relative topic is NSURLConnection, UIImageView.
I have done something similar before.
1. Create a new model inherit to UIView
2. This model will have a UIImageView, NSData
3. When u init the model, pass in a URL
4. Use NSURLConnection to send out AsynchroizedRequest
5. By using NSURLConnection delegate, you will finally get the Data of the image
6. Init a UIImage with these data
7. Init The UIImageView with this Image
8. Add this imageview to this model or directly pointing this model to the imageview
Feel free to ask for more detail :)

iphone showing images in tableView

Hi I have this code to download images from the server and then to show them in table view with some text (if there is no image I want to show the text anyway):
//download part
NSMutableString *photourl=[[NSMutableString alloc] initWithString:url_image];
[photourl appendString:[dic objectForKey:#"photo"]];
#try{
//add to mutable array
[photoSmallImageData addObject:[NSData dataWithContentsOfURL:[NSURL URLWithString:photourl]]];
}
#catch (id Exception) {
[photoSmallImageData addObject:[NSData dataWithContentsOfURL:[NSURL URLWithString:url_image]]];
}
//part of table view code
NSData *imageData = [photoSmallImageData objectAtIndex:indexPath.row];
#try {
cell.imageView.image = [UIImage imageWithData:imageData];
}
#catch (NSException *exception) {
cell.imageView.image = [UIImage imageNamed:#"icon72.png"];
}
It works but the thing is sometimes there is no image so I would like to replace it with some icon but since we can't add nil to array i juste add some part of my link there and then even if the link doesn't work for image data it doesn't call the catch method. I don't know if I could some how replace the url with local url but I don't know how. Also I can't skip the step because then the images will not correspond to the text next to it.
2.The images are all of diferent sizes and I would like them to have standard look and be centered is there a way to do that and at which point of the code? Thank you very much
EDIT I have found the method:
dataWithContentsOfURL:options:error:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/Reference/Reference.html
but I don't know how to use it
Why don't you create the images when the image data is downloaded and keep them in the array? Also, you are right to be after the dataWithContentsOfURL:options:error: method. Given that you keep the image names in the dic dictionary, the code would look like this:
NSMutableString *photourl=[[NSMutableString alloc] initWithString:url_image];
[photourl appendString:[dic objectForKey:#"photo"]];
//try to create the image
NSError* err = nil;
UIImage* img = nil;
NSData* tempData = [NSData dataWithContentsOfURL:[NSURL URLWithString:photourl] options:NSDataReadingMappedIfSafe error:&err];
if (!err) {
// image data loaded, create image
img = [UIImage imageWithData:tempData];
} else {
// image data could not be loaded, display error
NSLog(#"%#", err);
// put the default image in the array
img = [UIImage imageNamed:#"icon72.png"];
}
[photoSmallImage addObject:img];
And then in the table view delegate:
cell.imageView.image = [photoSmallImage objectAtIndex:indexPath.row];
As for the second part of your question, you may want to have a look at the contentMode property of UIView class. The default is scale to fill. By manipulating this property on the cell.imageView, you can achieve the effect that you desire.

xcode Caching images for images in UIScrollview

I have a list of images retrieve from .xml file and these images are images link from the server e.g. " www.seeimage.com/rice.png"
I am parsing the images everytime I went to that page
-(void)viewdidload{
for (int i = 0; i<[appDelegate.foodItems count];i++) {
NSURL *ZensaiimageSmallURL = [NSURL URLWithString:ZensaiPLUitems.ZensaiimageSmallURL];
NSString *string = [[NSString alloc] initWithFormat:#"%#", ZensaiimageSmallURL];
NSData *simageData = [NSData dataWithContentsOfURL:ZensaiimageSmallURL];
UIImage *itemSmallimage = [UIImage imageWithData:simageData];
[zenbutton2 setImage:itemSmallimage forState:UIControlStateNormal];
[scrollView addSubview:zenbutton2];
}
}
i have been trying out on this tutorial : http://www.iphonedevsdk.com/forum/iphone-sdk-tutorials/13315-image-caching-tutorial.html
but i have no idea on how to implement this in my work.
any idea on how to cache them on the first run and whenever i return to that view ?
i don't want to rerun this method to retrive the images from the website everytime i come to this view.
it takes quite some time to init the images from the website to my UIButton before populating them to the scrollview.
if you do not want to retrieve the images from the website every time then you can download it once and store them e.g. into NSMutableDictionary . And access it whenever you want.
OR
Parse those images in separate thread so that your table view or scroll view will not get paused.