I am new to Xcode (using 4.3) and am not sure how to save an image to the device's camera roll. All that I have done so far is set up an IBAction for the button to save the image. What library method or function can I use to save an image to the user's camera roll?
You use the UIImageWriteToSavedPhotosAlbum() function.
//Let's say the image you want to save is in a UIImage called "imageToBeSaved"
UIImageWriteToSavedPhotosAlbum(imageToBeSaved, nil, nil, nil);
Edit:
//ViewController.m
- (IBAction)onClickSavePhoto:(id)sender{
UIImageWriteToSavedPhotosAlbum(imageToBeSaved, nil, nil, nil);
}
Here's an answer for iOS8+ using the Photos framework.
Objective-C:
#import <Photos/Photos.h>
UIImage *snapshot = self.myImageView.image;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetChangeRequest *changeRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:snapshot];
changeRequest.creationDate = [NSDate date];
} completionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"successfully saved");
}
else {
NSLog(#"error saving to photos: %#", error);
}
}];
Swift:
// Swift 4.0
import Photos
let snapshot: UIImage = someImage
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAsset(from: snapshot)
}, completionHandler: { success, error in
if success {
// Saved successfully!
}
else if let error = error {
// Save photo failed with error
}
else {
// Save photo failed with no error
}
})
Here's a link to the Apple documentation.
Don't forget to add the appropriate key/value to your info.plist to request permission to access the photo library:
<key>NSCameraUsageDescription</key>
<string>Enable camera access to take photos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Enable photo library access to select a photo from your library.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Enable photo library access to save images to your photo library directly from the app.</string>
For reference, you can save videos in a similar manner:
UISaveVideoAtPathToSavedPhotosAlbum(videoPath, nil, nil, nil);
You might want to save a video to upload to Instagram, for example:
// Save video to camera roll; we can share to Instagram from there.
-(void)didTapShareToInstagram:(id)sender {
UISaveVideoAtPathToSavedPhotosAlbum(self.videoPath, self, #selector(video:didFinishSavingWithError:contextInfo:), (void*)CFBridgingRetain(#{#"caption" : caption}));
}
- (void) video: (NSString *) videoPath
didFinishSavingWithError: (NSError *) error
contextInfo: (void *) contextInfoPtr {
NSDictionary *contextInfo = CFBridgingRelease(contextInfoPtr);
NSString *caption = contextInfo[#"caption"];
NSString *escapedString = [videoPath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]; // urlencodedString
NSString *escapedCaption = [caption stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]; // urlencodedString
NSURL *instagramURL = [NSURL URLWithString:[NSString stringWithFormat:#"instagram://library?AssetPath=%#&InstagramCaption=%#", escapedString, escapedCaption]];
[[UIApplication sharedApplication] openURL:instagramURL];
}
Save the image in photo library in Swift 4
Add "Privacy - Photo Library Additions Usage Description" in info.plist
Then use following code to save image
UIImageWriteToSavedPhotosAlbum(imageView.image!, nil, nil, nil)
Use asImage() to get unique content to save to the camera roll.
If you use asImage() you can save a variety of fun things to the camera roll with just a few lines of code! This can be very powerful if the object has some transparency already incorporated.
asImage() works with UITextView, WKWebView, UIImageView, UIButton, UISlider, UITableView to name some objects (but they may need to be visible when you get the image (having a non zero alpha)). I even use it to capture tiling, but that's loaded into a UIImageView already in my design. I suspect asImage() may work with many more object types too, but I only tried the ones I mentioned.
If it's a UITextView, and you set the background color to .clear then the text gets saved with a transparent background. If your text contains emoji or Memoji, you can now get those images into the camera roll, or into UIImageViews internal to your app. Having Memoji/Emoji with a transparent background in your camera roll where they can be used in any variety of applications is powerful.
Other objects may have some transparency if you crop a photo image to a circle, or set the corner radius to clip off the corners.
Note in my code, pointerToTextObjectSelected is a UITextView
var pointerToTextObjectSelected = UITextView()
// above populated elsewhere
let thisObject = pointerToTextObjectSelected.asImage()
let imageData = thisObject.pngData()
let imageToSave = UIImage(data: imageData!)
UIImageWriteToSavedPhotosAlbum(imageToSave!, nil, nil, nil)
// You will need this extension :
extension UIView {
// Using a function since `var image` might conflict with an existing variable
// (like on `UIImageView`)
func asImage() -> UIImage {
if #available(iOS 10.0, *) {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
} else {
UIGraphicsBeginImageContext(self.frame.size)
self.layer.render(in:UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return UIImage(cgImage: image!.cgImage!)
}
}
}
Related
I have an iOS app which creates a PDF and is shared using the UIActivityViewController using the default share functionality. I had to block a few sharing facilities like Vimeo, Facebook, Twillio, etc. and also the file should not be stored on the device.
With the new iOS 11 version, Apple has provided SaveToFiles option with the default share functionality. I tried using excludedActivityTypes, and with blocking the "com.apple.CloudDocsUI.AddToiCloudDrive" option, but no success.
Can anyone help me to disable the SaveToFile option though Swift? I am using Xcode 9.3 and Swift 4.
Currently, we probably cannot disable Save to Files or Add to Shared Album by add excludedActivityTypes in activityViewController. But we can prevent when user did press two activity types will do not perform action instead we'll showing alert.
First we create custom UIActivityItemProvider
class ActivityItemProvider: UIActivityItemProvider {
override func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
// in here we'll check activityType = "com.apple.CloudDocsUI.AddToiCloudDrive" (Save to Files),
// activityType = "com.apple.mobileslideshow.StreamShareService" (Shared Album)
if(activityType.rawValue.contains("com.apple.CloudDocsUI.AddToiCloudDrive") || (activityType.rawValue.contains("com.apple.mobileslideshow.StreamShareService") {
// dismiss activityViewController first
activityViewController.dismiss(animated: true, completion: nil)
// show alert controller, we can using UIApplication.shared.keyWindow?.rootViewController to present alert
return nil
}
return self.placeholderItem
}
In where we call UIActivityController, let's using
let item = ActivityItemProvider.init(placeholderItem: {your item})
let activityViewController = UIActivityViewController.init(activityItems: item, applicationActivities: nil)
ObjectiveC version
#import "BRActivityItemProvider.h"
#import "UIViewController+Additions.h"
#implementation BRActivityItemProvider
- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(UIActivityType)activityType {
[super activityViewController:activityViewController itemForActivityType:activityType];
NSLog(#"itemForActivityType %#", activityType);
if([activityType containsString:#"com.apple.CloudDocsUI.AddToiCloudDrive"] || [activityType containsString:#"com.apple.mobileslideshow.StreamShareService"]) {
[activityViewController dismissViewControllerAnimated:true completion:^{
UIViewController *rootController = [UIApplication sharedApplication].keyWindow.rootViewController;
if(rootController != nil) {
NSString *str = [activityType containsString:#"com.apple.CloudDocsUI.AddToiCloudDrive"]
? #"Cannot save file to iCloud Driver"
: #"Cannot save file to Shared Album";
dispatch_async(dispatch_get_main_queue(), ^{
// this's my custom show alert controller, you can change your own
[rootController showAlertController:str action:^(UIAlertAction * _Nonnull action) {
}];
});
}
}];
return nil;
}
return self.placeholderItem;
}
In where call UIActivityViewController
BRActivityItemProvider *itemProvider = [[BRActivityItemProvider alloc]initWithPlaceholderItem:item];
UIActivityViewController *activityController = [[UIActivityViewController alloc]initWithActivityItems:itemProvider applicationActivities:nil];
activityController.popoverPresentationController.sourceView = controller.view;
// your logic code
// ....
Like in templeRun, we also wish to add inAp purchase to give image.
http://www.youtube.com/watch?v=dYog6yhs0y4
How can I download image placed inside game bundle to iphone's photo library?
Call UIImageWriteToSavedPhotosAlbum , which I believe looks like this:
UIImageWriteToSavedPhotosAlbum(
UIImage* image,
id completionTarget,
SEL completionSelector,
void* contextInfo
);
One way to use it is like this:
UIImage* myImage = [UIImage imageNamed:#"image.png"];
UIImageWriteToSavedPhotosAlbum(myImage, self, #selector(image:didFinishSavingWithError:contextInfo), nil);
You only need to set the completion target and completion selector if you want to be notified if it succeeded or failed. If so, don't forget to add this method to the class:
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void*)contextInfo
{
if (error)
{
// Do something on error... (like show a UIAlert for example)
}
else
{
// Do something on success... (maybe show a "Your image was saved" alert
}
}
The image doesn't save right away and might error in the process so this can be useful.
You can also use the ALAssetsLibrary, which I believe would look like this:
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:[myImage CGImage] orientation:(ALAssetOrientation)[myImage imageOrientation] completionBlock:^(NSURL* assetURL, NSError* error)
{
if (error)
{
// Do something on error... (like show a UIAlert for example)
}
else
{
// Do something on success... (maybe show a "Your image was saved" alert
}
}];
Hope this helped.
Create a UIImage object from the picture within your bundle using imageNamed: and then use UIImageWriteToSavedPhotosAlbum() to save it to the Photo Library.
UIImage *image = [UIImage imageNamed:#"wallpaper.jpg"];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
For more information on the options for UIImageWriteToSavedPhotosAlbum() (callback functions, etc.) you can take a look at the UIKit Function Reference.
I am having an app in which I am sharing an image on instagram and it works perfectly fine.
As shown in the screenshot. When I press the button share on instagram it opens up in UIActivityviewcontroller.
Is it possible that If I click on Share on Instagram and and it takes me directly to the native instagram app?
If the user has other apps installed in his device that apps also shows because of the UIActivityViewController.
I don't want to show UIActivityviewcontroller saying "Open In Instagram".
Thanks in advance.
Edited
I am taking a screenshot of a view and sharing that screenshot on Instagram.
When I click on a button share it opens up as shown in the screenshot which I don't want.
I am using the below code.
NSURL *instagramURL = [NSURL URLWithString:#"instagram://"];
if ([[UIApplication sharedApplication] canOpenURL:instagramURL])
{
CGRect rect = CGRectMake(0 ,0 , 0, 0);
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, self.view.opaque, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIGraphicsEndImageContext();
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
imagename=[NSString stringWithFormat:#"ff.ig"];
NSString* fullPathToFile = [documentsDirectory stringByAppendingPathComponent:imagename];
////NSLog(#"%#",fullPathToFile);
igImageHookFile = [[NSURL alloc] initWithString:[[NSString alloc] initWithFormat:#"file://%#", fullPathToFile]];
self.dic.UTI = #"com.instagram.photo";
self.dic = [self setupControllerWithURL:igImageHookFile usingDelegate:self];
self.dic=[UIDocumentInteractionController interactionControllerWithURL:igImageHookFile];
self.dic.annotation = [NSDictionary dictionaryWithObject:#"Image" forKey:#"InstagramCaption"];
[self.dic presentOpenInMenuFromRect: rect inView:self.view animated: YES ];
}
for swift
var instagramURL = NSURL(string: "instagram://app")!
if UIApplication.sharedApplication().canOpenURL(instagramURL) {
var documentDirectory = NSHomeDirectory().stringByAppendingPathComponent("Documents")
var saveImagePath = documentDirectory.stringByAppendingPathComponent("Image.igo")
var imageData = UIImagePNGRepresentation(self.bestScoreShareView.takeSnapshot(W: 640, H: 640))
imageData.writeToFile(saveImagePath, atomically: true)
var imageURL = NSURL.fileURLWithPath(saveImagePath)!
docController = UIDocumentInteractionController()
docController.delegate = self
docController.UTI = "com.instagram.exclusivegram"
docController.URL = imageURL
docController.presentOpenInMenuFromRect(CGRectZero, inView: self.view, animated: true)
} else {
println("instagram not found")
}
extension for take shapshot from uiview
extension UIView {
func takeSnapshot(#W: CGFloat, H: CGFloat) -> UIImage {
var cropRect = CGRectMake(0, 0, 600, 600)
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)
drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
image.imageWithNewSize(CGSizeMake(W, H))
return image
}
}
extension for set image size
extension UIImage {
func imageWithNewSize(newSize:CGSize) ->UIImage {
UIGraphicsBeginImageContext(newSize)
self.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage
}
}
self.documentInteractionController = [self setupControllerWithURL:imgurl usingDelegate:self];
self.documentInteractionController=[UIDocumentInteractionController interactionControllerWithURL:imgurl];
self.documentInteractionController.UTI = #"com.instagram.exclusivegram";
Use this code in same sequence . Here documentInteractionController is object of UIDocumentInteractionController.just for your knowledge. you will get instagram only in "open in" window.
Save your image with igo extension instead of ig.
By this you will only get instagram option in your "Open in" Menu.
Or if you want to open directly instagram when you press the button you can pass the direct url of the app and it will lead you to the native instagram app.
Hope it helps you.
Have you checked the size of your image.As it supports image bigger than 612*612 do check the size of your image you are posting
In my iPhone app, I am using the iPhone's camera to take a photo and save it do disk (the application's documents folder). This is how i save it:
[UIImageJPEGRepresentation(photoTaken, 0.0) writeToFile:jpegPath atomically:YES];
Using the most compression, I figured reading the image from disk would be quick. But its not! I use the image as the background image for a button in one of my views. I load it like this:
[self.frontButton setBackgroundImage:[UIImage imageWithContentsOfFile:frontPath] forState:UIControlStateNormal];
When I navigate to the view with this button, it is slow and choppy. How do I fix this?
+imageWithContentsOfFile: is synchronous, so the UI on your main thread is being blocked by the image loading from disk operation and causing the choppiness. The solution is to use a method that loads the file asynchronously from disk. You could also do this in a background thread. This can be done easily by wrapping the +imageWithContentsOfFile: in dispatch_async(), then a nested dispatch_async() on the main queue that wraps -setBackgroundImage: since UIKit methods need to be run on the main thread. If you want the image to appear immediately after the view loads, you'll need to pre-cache the image from disk so it's in-memory immediately when the view appears.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
UIImage *image = [UIImage imageWithContentsOfFile:frontPath];
dispatch_async(dispatch_get_main_queue(), ^{
[self.frontButton setBackgroundImage:image forState:UIControlStateNormal];
});
});
As an aside, if the button image happens a gradient, consider using the following properties to ensure the image file loaded from disk is tiny:
- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets
or (deprecated, only use if you need to support iOS 4.x):
- (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight
This is the faster way I know. You'll need to import #import <ImageIO/ImageIO.h>
I use this code to download and compress images during a scroll, inside a scrollview and you barely notice the delay.
CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef)mutableData, NULL);
CFDictionaryRef options = (CFDictionaryRef)[[NSDictionary alloc] initWithObjectsAndKeys:(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform, (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent, (id)[NSNumber numberWithDouble:200.0], (id)kCGImageSourceThumbnailMaxPixelSize, nil];
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options);
UIImage *image = [[UIImage alloc] initWithCGImage:thumbnail];
// Cache
NSString *fileName = #"fileName.jpg";
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:#"thumbnail"];
path = [path stringByAppendingPathComponent:fileName];
if ([UIImagePNGRepresentation(image) writeToFile:path atomically:YES]) {
// Success
}
I face a very similar issue where I had to load hundreds of images from the directory. My performance was quite slow if I used UIImage(contentsOfFile:) method. The below method increased my performance to 70 %.
class ImageThumbnailGenerator: ThumbnailGenerator {
private let url: URL
init(url: URL) {
self.url = url
}
func generate(size: CGSize) -> UIImage? {
guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil) else {
return nil
}
let options: [NSString: Any] = [
kCGImageSourceThumbnailMaxPixelSize: Double(max(size.width, size.height) * UIScreen.main.scale),
kCGImageSourceCreateThumbnailFromImageIfAbsent: true
]
return CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as NSDictionary).flatMap { UIImage(cgImage: $0) }
}
}
I'm new to iphone. In my small application, how to get image path from photo library. I use this following code to getting images and placed in imageview.
-(IBAction) selectimage
{
UIImagePickerController *picker=[[UIImagePickerController alloc] init];
picker.delegate=self;
picker.sourceType=UIImagePickerControllerSourceTypeSavedPhotosAlbum;
[self presentModalViewController:picker animated:YES];
[picker release];
}
-(void) imagePickerController:(UIImagePickerController *)UIPicker didFinishPickingMediaWithInfo:(NSDictionary *) info
{
[UIPicker dismissModalViewControllerAnimated:YES];
imageview.image=[info objectForKey:"UIImagePickerControllerOriginalImage"];
NSLog("Image Path=%#",imageview.image);
}
Its succefully placed to Imageview. But i cant get its imagepath. I'm using nslog("%#",imageview.image); it shows "Image Path=" in console.
How can i get its path and how to retrieve photos from photo library. Any body help me. Thanks in advance.
UIImagePickerControllerOriginalImage returns only the image's data, with no reference to its local storage. I haven't used it myself but I guess what you are looking for is
NSURL* localUrl = (NSURL *)[info valueForKey:UIImagePickerControllerReferenceURL];
At least, it's the only way I know of to retrieve any URL.
To retrieve the image, have a look at this question. But also keep in mind, that you already have the image retrieved with
UIImage* image=(UIImage*)[info objectForKey:#"UIImagePickerControllerOriginalImage"];
so ask yourself if you really need this.
Swift 3.0, You can get imageData from referenceURL by following way
let url = URL(string: "assets-library://asset/asset.JPG?id=1000000007&ext=JPG")
let asset = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil)
guard let result = asset.firstObject else {
return nil
}
let imageManager = PHImageManager.default()
var imageData: Data? = nil
imageManager.requestImageData(for: result, options: nil, resultHandler: { (data, string, orientation, dict) in
imageData = data
})