requestImageDataForAsset result handler improperly formatted - ios

I am trying to request the full image from a PHAsset.
My code is as follows
#IBAction func nextTap(sender: AnyObject)
{
for asset in _selectedAssets
{
PHImageManager.defaultManager().requestImageDataForAsset(asset, options: nil)
{
imageData,dataUTI,orientation,info in
{
println("worked")
}
}
}
}
I am getting this error though:
I have read the documentation on this call (https://developer.apple.com/library/IOs/documentation/Photos/Reference/PHImageManager_Class/index.html#//apple_ref/occ/instm/PHImageManager/requestImageDataForAsset:options:resultHandler:)
And I have, I think, followed the trailing syntax closure required, and put in the appropriate parameters.
I watched the WWDC 2014 video on this as well.
Cannot seem to get this to work for the full image.
If I want a smaller sized image, I used this code:
PHImageManager.defaultManager().requestImageForAsset(asset, targetSize:_cellSize, contentMode: .AspectFill, options: nil)
{
result, info in
if reuseCount == cell.reuseCount
{
cell.imageView.image = result
cell.imageView.frame = CGRectMake(0, 0,self._cellSize.width,self._cellSize.height)
}
}
And this works.
So Im not sure what I am doing wrong in my fist call but would love some insight in to how to request a full size image from a PHAsset and/or whats wrong with my syntax in the first code block.
Thanks
Update:
Here is the error message
"Cannot invoke 'println()' with an argument list type of '(PHAsset, options:NilLiteralConvertible, (($T6, ($T6,$T7,$T8,($T6,$T7,$T8)...."

It turns out the brackets after the 'in' screw everything up.
Removing those brackets fixed the problem
for asset in _selectedAssets
{
PHImageManager.defaultManager().requestImageDataForAsset(asset, options: nil)
{
imageData,dataUTI,orientation,info in
println("worked")
}
}

This works for me:
PHImageManager.defaultManager().requestImageDataForAsset(asset, options: requestOptions) {
(imageData: NSData!, dataUTI: String!, orientation: UIImageOrientation, info: [NSObject : AnyObject]!) -> Void in
println("Yay!")
}

Related

iOS - Swift 3 Share Extension Preview Image

I'm currently building a share extension that accepts URLs. As part of this I've customised my share screen as outlined in a previous question to create a full screen view controller. This is all working fine. However in the default share composer view I noticed there was a preview image of the web page. I'm trying to access this in my extension but I can't seem to get hold of it.
Specifically I've been trying to use the method
loadPreviewImage
https://developer.apple.com/reference/foundation/nsitemprovider/1403925-loadpreviewimage
You will note in the docs that this says the following for the completion handler
completion​Handler
A completion handler block to execute with the results. The first parameter of this block must be a parameter of type NSData, NSURL, UIImage (in iOS), or NSImage (in macOS) for receiving the image data. For more information about implementing the block, see Completion​Handler.
However if I try to set this as a UIImage in my completion block I get an error of
Cannot convert value of type '(UIImage, _) -> ()' to expected argument
type 'NSItemProvider.CompletionHandler!'
example code where itemProvider is confirmed to be an instance of NSItemProvider via guard statements
itemProvider.loadPreviewImage(options: nil) { (image: UIImage, error) in
}
The docs for the completion Handler say to set this to what type you want and it will attempt to coerce the data to the type you specify. Has anyone seen this before? I'm not sure what to do here as I can't see what I'm doing wrong.
https://developer.apple.com/reference/foundation/nsitemprovider/completionhandler
If all else fails I'll look at using some Javascript to get an image from the dom but I would have liked the preview image that Apple seemed to provide
I don't know why the code in
itemProvider.loadPreviewImage(options: nil) { (image: UIImage, error) in
}
not called when Post button tapped.
My round way is saving the preview image in method
override func configurationItems() -> [Any]! {
}
as
let inputItem: NSExtensionItem = self.extensionContext?.inputItems[0] as! NSExtensionItem
let itemProvider = inputItem.attachments![0] as! NSItemProvider
if (itemProvider.hasItemConformingToTypeIdentifier("public.url")) {
itemProvider.loadPreviewImage(options: nil, completionHandler: { (item, error) in // 画像を取得する
if let image = item as? UIImage {
if let data = UIImagePNGRepresentation(image) {
self.photoNSURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("preview.png") as NSURL!
do {
try data.write(to: self.photoNSURL as URL, options: .atomic)
} catch {
print("\(#file)#\(#function)(\(#line)): error: \(error.localizedDescription)")
}
}
}
})
}

PHCacheImageManager.requestAVAsset(forVideo:...) returns nil AVAsset

I'm using [PHCachingImageManager] to cache videos in a collection view. I can display the images generated with the cache manager, but pulling AVAssets isn't working.
I try to get an AVAsset by using the .requestAVAsset(forVideo:...) method but the [asset] that is returned to the result handler is nil. I get an error when trying to unwrap the returned [asset].
I also looked at the [audioMix] and [info] parameters returned to the result handler and they were nil was well. Does anyone know what I'm doing wrong/missing here?
viewDidLoad() {
let videosAlbum = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumVideos, options: nil)
self.videoAssets = PHAsset.fetchAssets(in: videosAlbum.object(at: 0), options: nil)
self.imgCacheManager.startCachingImages(for: self.videoAssets.objects(at: [0, videoAssets.count-1]), targetSize: CGSize(width: 150, height: 150), contentMode: .aspectFit, options: nil)
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedAsset = self.imgCacheManager.requestAVAsset(forVideo: self.videoAssets.object(at: indexPath.item), options: nil, resultHandler: {
asset,_,_ in
self.delegate.selectedVideo(Asset: asset!) // this is the line I get the error
})
}
My delegate is an optional but for sure has something in it, so I'm confident it's the unwrapped [asset] that's throwing the error.
It looks like I was trying to request video assets that were stored in iCloud without setting the video request options to allow network access.
When requesting the video from the PHCachingImageManager, I failed to pass any PHVideoRequestOptions object. Adding the lines below just prior to requesting the asset fixes this.
let options = PHVideoRequestOptions()
options.isNetworkAccessAllowed = true
I found this thread on an open source image picker using the Photos framework. The videos I was testing with were indeed in iCloud which caused my returned asset to be nil. The linked thread also has a good example of hooking up a progress handler in objective C:
options.networkAccessAllowed = YES;
options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info)
{
...
};

Import PDF into own App with iOS Action Extension

I'm looking for a possibility to import a PDF in order to do some further tasks with it, just like described in this Question: Importing PDF files to app
After two days of looking around in the inter webs I found that an action extension might be the solution, this is how far I got:
override func viewDidLoad() {
super.viewDidLoad()
let fileItem = self.extensionContext!.inputItems.first as! NSExtensionItem
let textItemProvider = fileItem.attachments!.first as! NSItemProvider
let identifier = kUTTypePDF as String
if textItemProvider.hasItemConformingToTypeIdentifier(identifier) {
textItemProvider.loadItemForTypeIdentifier(identifier, options: nil, completionHandler: handleCompletion)
}
}
func handleCompletion(pdfFile: NSSecureCoding?, error: NSError!) {
print("PDF loaded - What to do now?")
}
The completion handler is called properly so I assume the PDF is loaded - but then I don't now how to proceed. If the action extension only handles images or text it could easily be downcasted, but the only way to work with files I know is with path names - which I do not have and don't know how to obtain. Plus, I'm pretty sure Sandboxing is also part of the party.
I guess I only need a push in the right direction which Class or Protocol could be suitable for my need - any suggestions highly appreciated.
For anyone else looking for an answer - I found out by myself, and it's embarrassingly easy:
func handleCompletion(fileURL: NSSecureCoding?, error: NSError!) {
if let fileURL = fileURL as? NSURL {
let newFileURL = NSURL(fileURLWithPath: NSTemporaryDirectory().stringByAppendingString("test.pdf"))
let fileManager = NSFileManager.defaultManager()
do {
try fileManager.copyItemAtURL(fileURL, toURL: newFileURL)
// Do further stuff
}
catch {
print(error)
}
}
}

How to display images that in photo gallery that were just selected?

Goal: The user selects photos from her photo gallery > Tap "Done" > Photos show up.
Problem: The photos don't show up immediately after "Done". They do show up if I open the picker again and tap "Cancel."
This worked fine (photo showed up instantly) when I was using UIImagePickerController, but then I switched to ELCImagePickerController for multiple selection and it no longer works.
Hunches
CollectionView that displays all images is not being reloaded? But I tried calling collectionView.reloadData() after the images are selected (on the completionHandler). This did not work.
If helpful, this is my elcImagePickerController function from my ViewController.swift
func elcImagePickerController(picker: ELCImagePickerController!, didFinishPickingMediaWithInfo info: [AnyObject]!) {
println("inside elcImagePickerController")
self.dismissViewControllerAnimated(true, completion: nil)
if (info.count == 0) {
return
}
var pickedImages = NSMutableArray()
for any in info {
let dict = any as! NSMutableDictionary
let image = dict.objectForKey(UIImagePickerControllerOriginalImage) as! UIImage
pickedImages.addObject(image)
}
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0), {
PHPhotoLibrary.sharedPhotoLibrary().performChanges(
{
for img in pickedImages{
// Request creating an asset from the image
// create multiple creationRequestForAssetFromImage
let createAssetRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(img as! UIImage) // add to main gallery
// / Request editing the album
let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection, assets: self.photosAsset)
// Get a placeholder for the new asset and add it to the album editing request
let assetPlaceholder = createAssetRequest.placeholderForCreatedAsset
albumChangeRequest.addAssets([assetPlaceholder])
}
}, completionHandler: {(success, error) in
NSLog("Adding Image to Library -> %#", (success ? "Success" : "Error"))
picker.dismissViewControllerAnimated(true, completion: nil)
}
)
})
It took me a while to duplicate your scenario and error, but I managed and I found a solution!
You were on the right path with calling collectionView.reloadData() in the completionHandler, but it needed a bit more work.
1)
In my testing scenario, I've used a PHFetchResult called photosAsset as the main source for the UICollectionView (I'm presuming you have a similar setup).
You have to update this PHFetchResult to reflect the recent changes, in my case I used the following line:
self.photosAsset = PHAsset.fetchAssetsInAssetCollection(self.assetCollection, options: nil)
2)
It's correct that you have to reload the view with collectionView.reloadData(), but the code inside of the completionHandler is not run on the main thread/UI thread. So you have to call it like the following:
dispatch_async(dispatch_get_main_queue(), {self.collectionView?.reloadData()})
Sum up:
In final, your completionHandler should look something similar to this:
completionHandler: {(success, error) in
NSLog("Adding Image to Library -> %#", (success ? "Success" : "Error"))
self.photosAsset = PHAsset.fetchAssetsInAssetCollection(self.assetCollection, options: nil)
dispatch_async(dispatch_get_main_queue(), {self.collectionView?.reloadData()})
picker.dismissViewControllerAnimated(true, completion: nil)
}

Loading images from Parse using Swift

I've written my code for retrieving images from my Parse data console so that they will appear in my Storyboard for my Swift app, but I'm not sure how to make an IBOutlet connect to the UIImageView I added to my Storyboard so that the image from Parse will actually appear in its place. Here's the code I have so far in my ViewController:
var imageResources : Array<UIImage> = []
override func viewDidLoad() {
super.viewDidLoad()
func loadImages(){
var query2 = PFQuery(className: "VoteCount")
query.findObjectsInBackgroundWithBlock ({(objects:[AnyObject]!, error: NSError!) in
if(error == nil){
let imageObjects = objects as [PFObject]
for object in objects {
let thumbNail = voteCount1["image"] as PFFile
thumbNail.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
let image = UIImage(data:imageData)
self.imageResources.append(image!)
println(image)
}
})
}
}
else{
println("Error in retrieving \(error)")
}
})
}
}
What kind of IBOutlet do I make that connects to the UIImageView I've added to my Main.storyboard? I'm just not sure what to name it that will call the image from Parse that I'm trying to retrieve.
control-drag from your UIImageView to your Swift file, outside the methods but inside the implementation. name it myImageView.
You can't call println on an image.
Then after the line if (error == nil) { try the following
if let image = UIImage(data:imageData) {
dispatch_async(dispatch_get_main_queue()) {
self.myImageView.image = image
}
}
Or something to that effect. You need to be sure:
the image exists
you do anything with UI updates on the main thread (that's what dispatch_async(dispatch_get_main_queue()) {} accomplishes
Try it and let me know if that helps. I'm on the road right now but when I get a chance to sit I'll circle back and check this out.
One last trick: you can put a breakpoint on lines where you get an image. Then when the application pauses, you can hover over the word image in the line if let image = UIImage(data:imageData) { and click on the eye icon in the popup. You should then be able to view the image from Parse. If the image doesn't appear when you click the eye icon, you probably have additional debugging to do (does the image exist, is your logic up to that point correct, etc).

Resources