The current best solution:
https://stackoverflow.com/a/21888830/1786820
I am trying to do one better, by opening the Instagram app with a preselected video file from the PhotoRoll, and a preloaded caption. In the Flipagram app, they do just this. When you hit share on a video, they save it your camera roll, suggest a caption, then direct to the Instagram app photo selection screen, with the video preselected. Even if the video is not the latest media in the PhotoRoll, it correctly highlights the correct video, along the caption prepared in the Flipagram app.
Is this possibly an undocumented iPhone hook?
Any help is appreciated.
I came up with the idea to allow my app to accept the instagram:// URL schema. The hook from Flipagram opened up in my app as the following:
instagram://library?AssetPath=assets-library%3A%2F%2Fasset%2Fasset.mp4%3Fid%3D8864C466-A45C-4C48-B76F-E3C421711E9D%26ext%3Dmp4&InstagramCaption=Some%20Preloaded%20Caption
The undocumented iPhone hook that allows you to automatically select assets from the iPhones photo roll, and preload a caption for the video. This should give you the same user experience that Flipagrams app has with sharing a video to Instagram.
NSURL *videoFilePath = ...; // Your local path to the video
NSString *caption = #"Some Preloaded Caption";
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:[NSURL URLWithString:videoFilePath] completionBlock:^(NSURL *assetURL, NSError *error) {
NSURL *instagramURL = [NSURL URLWithString:[NSString stringWithFormat:#"instagram://library?AssetPath=%#&InstagramCaption=%#",[assetURL absoluteString].percentEscape,caption.percentEscape]];
if ([[UIApplication sharedApplication] canOpenURL:instagramURL]) {
[[UIApplication sharedApplication] openURL:instagramURL];
}
}];
Works great!
Update:
Instagram has removed the ability to pass the caption to their app. The best solution now it to just copy the desired caption to the paste board.
The answer is that it is not pulling the video from the camera roll at all, it might just look like it is.
Documentation here: http://instagram.com/developer/mobile-sharing/iphone-hooks/
The relevant bit is the bottom section "Document Interaction".
You would do this by doing something like this:
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"instagram.igo"];
NSData *data = // set this yourself
NSError *error = nil;
if (! [data writeToFile:filePath options:NSDataWritingAtomic error:&error])
{
// error here
}
self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
self.documentInteractionController.delegate = self;
self.documentInteractionController.UTI = #"com.instagram.exclusivegram";
self.documentInteractionController.annotation = #{ #"InstagramCaption" : #"caption text here" };
const BOOL couldOpen = [self.documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:myView animated:YES];
Set the data, the caption, and the view to present from yourself. Notice the UIDocumentInteractionController is also a property. It should be retained somewhere and not just a local variable in a method because it needs to exist outside of that scope when the method completes.
Related
I am working on an app where I need to upload videos to server.Now here I have 2 things:
Shoot video using UIImagePickerController,generate a thumbnail and then upload to server
Pick video from Photos gallery, generate thumbnail and then upload to server.
Now the only difference between the two is:
When I use 'generateImageAsynchronouslyForTimes:completionHandler:' method,I get a call in its completionHandler block and I get an AVAsset.Now I am using below code to get its URL:
NSURL *path_url = [(AVURLAsset*)asset URL];
This is where I think things are getting messed up because I am getting something like this in case 2(when I pick video from gallery):
file:///var/mobile/Media/DCIM/102APPLE/IMG_2439.mp4
So I can't upload it while case 1 is is working fine.Is it something related to sandbox?
What's the difference between these 2 paths?
file:///private/var/mobile/Containers/Data/Application/DA4632E3-FA25-4EBE-9102-62495BF105BF/tmp/trim.07786CFE-2477-4146-9EA0-0A04042A8D05.MOV"
file:///var/mobile/Media/DCIM/102APPLE/IMG_2439.mp4
I guess its appSandbox path in 1)
In iOS, every app is like an island and there is a sandbox environment for it.So if you like to upload your video that is not in your sandbox,you will have to copy that video to your sandbox and then you can upload it.This is how you can do this:
NSURL *path_url = [(AVURLAsset*)asset URL];
PHAssetResource *asset_resource = [[PHAssetResource assetResourcesForAsset:[fetchResult lastObject]]firstObject];
PHAssetResourceRequestOptions *options = [PHAssetResourceRequestOptions new];
options.networkAccessAllowed = YES;
NSURL *newURL = [self getSandboxURLFromURL:path_url];
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:asset_resource toFile:newURL options:options completionHandler:^(NSError * _Nullable error) {
//here you will get the newURL that you will use...
}];
//method to get sandbox URL
-(NSURL*)getSandboxURLFromURL:(NSURL*)photos_gallery_url{
NSString *last_path_component = [photos_gallery_url lastPathComponent];
NSString *pathToWrite = [NSTemporaryDirectory() stringByAppendingString:last_path_component];
NSURL *localpath = [NSURL fileURLWithPath:pathToWrite];
return localpath;
}
After I export an AVMutableComposition I use PHPhotoLibrary to save the video to the camera roll. In the creationRequestForAssetFromVideoAtFileURL: completion handler, I then open the saved video in Instagram, like so:
__block PHObjectPlaceholder *videoAssetPlaceholder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetChangeRequest *req = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:localVideoURL];
videoAssetPlaceholder = req.placeholderForCreatedAsset;
} completionHandler:^(BOOL success, NSError *error) {
if (success) {
completion(YES);
NSString *localID = videoAssetPlaceholder.localIdentifier;
NSRange rangeOfSlash = [localID rangeOfString:#"/"];
if (rangeOfSlash.location != NSNotFound) {
NSString *assetID = [localID substringToIndex:rangeOfSlash.location];
NSURL *instagramURL = [NSURL URLWithString:[NSString stringWithFormat:#"instagram://library?AssetPath=%#", assetID]];
if ([[UIApplication sharedApplication] canOpenURL:instagramURL]) {
[[UIApplication sharedApplication] openURL:instagramURL];
}
}
}
}];
About 50% of the times Instagram opens and the video plays like expected. The other 50% of the times, however, both the video and the preview is white, and all I get is the sound. This usually gets fixed by selecting another video and then going back to my video. The video plays perfectly in the camera roll, it's only Instagram that causes problems. Is this an issue that Instagram has or could I be exporting my videos the wrong way?
These are my AVAssetExportSession settings:
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition
presetName:AVAssetExportPresetHighestQuality];
exporter.outputURL = url;
exporter.outputFileType = AVFileTypeMPEG4;
exporter.shouldOptimizeForNetworkUse = YES;
exporter.videoComposition = mainCompositionInst;
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
handler(exporter.outputURL);
});
}];
Just heard from Apple DTS. They also agree this points to an Apple iOS bug and asked me to log it.
I cut out usage of AVAssetExportSession like mentioned above and it solved my problem as a work around. So the issue seems to be around that method which is probably contained in the Instagram method you are using.
So until Apple fixes this or Instagram builds a work around, there does not seem to be a solution for this problem... Bummer
This appears easy, but the lack of documentation makes this question impossible to guess.
I have pictures and videos on my app's icloud drive and I want to create thumbnails of these assets. I am talking about assets on iCloud Drive, not the iCloud photo stream inside the camera roll. I am talking about the real iCloud Drive folder.
Creating thumbnails from videos are "easy" compared to images. You just need 2 weeks to figure out how it works, having in mind the poor documentation Apple wrote but thumbnails from images seem impossible.
What I have now is an array of NSMetadataItems each one describing one item on the iCloud folder.
These are the methods I have tried so far that don't work:
METHOD 1
[fileURL startAccessingSecurityScopedResource];
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
__block NSError *error;
[coordinator coordinateReadingItemAtURL:fileURL
options:NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly
error:&error
byAccessor:^(NSURL *newURL) {
NSDictionary *thumb;
BOOL success = [newURL getResourceValue:&thumb forKey:NSURLThumbnailDictionaryKey error:&error];
UIImage *thumbnail = thumb[NSThumbnail1024x1024SizeKey];
}];
[fileURL stopAccessingSecurityScopedResource];
The results of this method are fantastic. Ready for that? Here we go: success = YES, error = nil and thumbnail = nil.
ANOTHER METHOD
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:fileURL
options:nil];
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
imageGenerator.appliesPreferredTrackTransform = YES;
CMTime time = CMTimeMake(0, 60); // time range in which you want
NSValue *timeValue = [NSValue valueWithCMTime:time];
[imageGenerator generateCGImagesAsynchronouslyForTimes:#[timeValue] completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * error) {
thumbnail = [[UIImage alloc] initWithCGImage:image];
}];
error = The requested URL was not found on this server. and thumbnail = nil
This method appears to be just for videos. I was trying this just in case. Any equivalent of this method to images?
PRIMITIVE METHOD
NSData *tempData = [NSData dataWithContentsOfUrl:tempURL];
NOPE - data = nil
METHOD 4
The fourth possible method would be using ALAsset but this was deprecated on iOS 9.
I think that all these methods fail because they just work (bug or not) if the resource is local. Any ideas on how to download the image so I can get the thumbnail?
Any other ideas?
thanks
EDIT: after several tests I see that Method 1 is the only one that seems to be in the right direction. This method works poorly, sometimes grabbing the icon but most part of the time not working.
Another point is this. Whatever people suggests me, they always say about downloading the whole image to get the thumbnail. I don't think this is the way to go. Just see how getting thumbnails of video work. You don't download the whole video to get its thumbnail.
So this
question remains open.
The Photos-Framework or AssetsLibrary will not work here as you would have to import your iCloud Drive Photos first to the PhotoLibrary to use any methods of these two frameworks.
What you should look at is ImageIO:
Get the content of the iCloud Drive Photo as NSData and then proceed like this:
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL );
NSDictionary* thumbOpts = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageAlways,
[NSNumber numberWithInt:160],(id)kCGImageSourceThumbnailMaxPixelSize,
nil];
CGImageRef thumbImageRef = CGImageSourceCreateThumbnailAtIndex(source,0,(__bridge CFDictionaryRef)thumbOpts);
UIImage *thumbnail = [[UIImage alloc] initWithCGImage: thumbImageRef];
After testing several solutions, the one that seems to work better is this one:
[fileURL startAccessingSecurityScopedResource];
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
__block NSError *error;
[coordinator coordinateReadingItemAtURL:fileURL
options:NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly
error:&error
byAccessor:^(NSURL *newURL) {
NSDictionary *thumb;
BOOL success = [newURL getResourceValue:&thumb forKey:NSURLThumbnailDictionaryKey error:&error];
UIImage *thumbnail = thumb[NSThumbnail1024x1024SizeKey];
}];
[fileURL stopAccessingSecurityScopedResource];
This solution is not perfect. It will fail to bring the thumbnails sometimes but I was not able to find any other solution that works 100%. Others are worst than that.
This works for me.It has a little bit different
func genereatePreviewForOnce(at size: CGSize,completionHandler: #escaping (UIImage?) -> Void) {
_ = fileURL.startAccessingSecurityScopedResource()
let fileCoorinator = NSFileCoordinator.init()
fileCoorinator.coordinate(readingItemAt: fileURL, options: .immediatelyAvailableMetadataOnly, error: nil) { (url) in
if let res = try? url.resourceValues(forKeys: [.thumbnailDictionaryKey]),
let dict = res.thumbnailDictionary {
let image = dict[.NSThumbnail1024x1024SizeKey]
completionHandler(image)
} else {
fileURL.removeCachedResourceValue(forKey: .thumbnailDictionaryKey)
completionHandler(nil)
}
fileURL.stopAccessingSecurityScopedResource()
}
}
It looks like you are generating your thumbnail after the fact. If this is your document and you are using UIDocument, override fileAttributesToWriteToURL:forSaveOperation:error: to insert the thumbnail when the document is saved.
Thanks for reading. I've created a GIF using methods from this question:
Create and and export an animated gif via iOS?
I'm trying to use the only method that appears to be able to save non JPG/PNG images to the camera roll, ALAssetLibrary's writeImageDataToSavedPhotosAlbum:metadata:completionBlock:
I save the Gif to the temp Directory like this:
NSString *exportPath = [NSTemporaryDirectory() stringByAppendingString:#"/animated.gif"];
NSURL *fileURL = [NSURL fileURLWithPath:exportPath isDirectory:NO];
Then access the NSData like:
NSData * gifData = [NSData dataWithContentsOfFile:fileURL.absoluteString];
The GIF is created as I'm able to display it in a UIImageView, but when I try to save it, the method returns as a success (no error) but doesn't actually save (returns Nil for the NSURL * assetURL and does not appear in the camera roll).
How can I get my GIFs to save successfully to the camera roll?
**
Solution 01 : Only saving the existing GIF file to Camera Roll
**
As I understand your problem. You are able to generate a GIF file but cannot save and also view it to the Camera Roll.
So I am attaching a sample test using existing GIF File.
Step 01. I copied a gif IMG_0009.GIF file in my Application Document directory.
Step 02 Than I use the below code to load this files NSData:
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:#"IMG_0009.gif"];
NSData *gifData = [NSData dataWithContentsOfFile:[fileURL path]];
Step 03: Now I save the file in the Media Directory:
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageDataToSavedPhotosAlbum:gifData metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) {
NSLog(#"Success at %#", [assetURL path] );
}];
The Asset URL is proper. Now you can check you media directory. you can locate the saved gif image.
Have Fun :)
**
Solution 02: Demo of Creating and saving GIF to Camera roll
**
I cloned some solution to show creating and saving of GIF files to Camera Roll.
You can download and check my fork at github:
The demo creates a GIF file by taking 2 or more images and save in the Camera Roll Directory
https://github.com/bllakjakk/Giraffe
The main Code to focus is like below:
[export encodeToFile:tempFile callback:^(NSString * aFile) {
NSLog(#"Path: %#", aFile);
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
NSData *data = [NSData dataWithContentsOfURL:[[NSURL alloc]initFileURLWithPath:aFile]];
[library writeImageDataToSavedPhotosAlbum:data metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) {
NSLog(#"Success at %#", [assetURL path] );
}];
}];
It uses the library as I mentioned in my solution before http://jitsik.com/wordpress/?p=208
How to verify:
Step 01: Run the demo project.
Step 02: As directed by the application add 2 images and click Export.
Step 03: Now check the camera roll you will find the created gif.
Previous:
GIF is a proprietary format, so you would need a 3rd party lib to save it.
check following link: http://jitsik.com/wordpress/?p=208
I found the issue was that I was unable to actually grab the GIF from the file. I switched from using CGImageDestinationCreateWithURL to CGImageDestinationCreateWithData and used a CFMutableDataRef to hold the Gif data. I don't know why, but that made saving to camera roll with writeImageDataToSavedPhotosAlbum work.
Has this been updated to work with iOS 9, and the deprecation of ALAssets? I do not see similar calls in PHPhotoLibrary.
Here is an updated answer using PHPhotoLibrary, since ALAssetsLibrary is deprecated.
I used this answer from another user - 陈星旺
PHPhotoLibrary save a gif data
NSString *exportPath = [NSTemporaryDirectory() stringByAppendingString:#"/animated.gif"];
NSURL *fileURL = [NSURL fileURLWithPath:exportPath isDirectory:NO];
NSData * gifData = [NSData dataWithContentsOfFile:fileURL.absoluteString];
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init];
[[PHAssetCreationRequest creationRequestForAsset]
addResourceWithType:PHAssetResourceTypePhoto
data:gifData
options:options];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (success) {
NSLog(#"image saved!");
} else {
NSLog(#"error saving image - %#", error ? error.localizedDescription : #"");
}
}];
If you needed to download the GIF data from a URL, you could use this:
NSData *gifData = [NSData dataWithContentsOfURL:theGIFsURL];
I'm building an iOS app that allows the user to upload videos from UIImagePickerController, either by recording or choosing them from the Camera Roll, as well as also play the chosen video. My question is, how would I go about keeping a reference to the videos that have been chosen this way? I want to do this so that if the video is still present on the device, I can use the local file rather than streaming the uploaded file.
When
imagePickerController:didFinishPickingMediaWithInfo:
returns, the URL in:
[info objectForKey:UIImagePickerControllerMediaURL];
Is in the format of: "file://localhost/private/var/mobile/Applications/ /tmp//trim.z2vLjx.MOV"
I'm lead to believe that the "/tmp/" directory is temporary, and therefore not suitable to save the URL for that location.
I can get all of the videos on the device through ALAssetsLibrary, but because I don't have a way of distinguishing them, this doesn't help me. I've been attempting to use:
[result valueForProperty:ALAssetPropertyDate];
To distinguish the videos, but I need a way of getting the creation date from UIImagePickerController for this to be useful.
I've finally managed to find a solution:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if(CFStringCompare((CFStringRef) mediaType, kUTTypeMovie, 0) == kCFCompareEqualTo)
{
//Dismiss the media picker view
[picker dismissModalViewControllerAnimated:YES];
//Get the URL of the chosen content, then get the data from that URL
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
NSData *webData = [NSData dataWithContentsOfURL:videoURL];
//Gets the path for the URL, to allow it to be saved to the camera roll
NSString *moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] path];
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum (moviePath))
{
ALAssetsLibrary *lib = [[ALAssetsLibrary alloc] init];
//The key UIImagePickerControllerReferenceURL allows you to get an ALAsset, which then allows you to get metadata (such as the date the media was created)
[lib assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL] resultBlock:^(ALAsset *asset) {
NSLog(#"created: %#", [asset valueForProperty:ALAssetPropertyDate]);
} failureBlock:^(NSError *error) {
NSLog(#"error: %#", error);
}];
}
}
As per usual, the solution was found by reading the documentation a little more thoroughly. Hopefully this'll help someone else out at some point.
You can easily keep a record of the videos you have on the device. Either by keeping a data base (which I think would be too much) or just a file with a list of your videos. In that list, you could have the URL of the assets.