Load image from the file after rewrite it - UIImage - ios

I have silly problem with loading image from the file. I have two views putted to UITabBarController.
At the first view user can load his image from the Photo Library or Camera. Second view present this photo. This file is on the server. If user doesn't choose his image server sent custom image. If there is uploaded photo it will push user's picture.
When user tap button there is a menu with options. For example we will decide to take picture from the Photo Library. After user took image:
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
self.saveUserImage(userID, imageData: UIImagePNGRepresentation(image)!)
apiManager.userUploadProfile(userID, imageData: UIImagePNGRepresentation(image)!)
userImageView.image = image
}
func saveUserImage(userUUID: String, imageData: NSData) {
let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last
let savePath = path! + "/\(userUUID)-user.png"
NSFileManager.defaultManager().createFileAtPath(savePath, contents: imageData, attributes: nil)
}
After that point user can see chosen picture and everything is okey. When user change tab, second view will refresh all data on it and again will download all images and data from the server. Unfortunately images that contains old user image doesn't refresh and there is still old photo.
When we come back to the first tab image is going back to old image but after few seconds.
The strangest thing is if I am checking server there is new uploaded image and in the app container it exist too. When I restart the app everything works perfectly.
It looks like this image is saved in the memory and iOS takes old version from RAM or from some other swap. How refresh UIImageView and show current saved image?
EDIT:
There is a method which load the image
func userProfilePicture(userId: String) -> UIImage {
let cacheDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last
let savePath = cacheDirectory! + "/\(userId)-user.png"
if NSFileManager.defaultManager().fileExistsAtPath(savePath) {
if let image = UIImage(named: savePath) {
return image
}
}
return UIImage(named: "test_avatar")!
}

I can't comment but i was facing a similar issue but i have a question, does the picture get uploaded before the user changes tabs, or after? Even if you call the function to update the database, it takes some time to actually "send the new photo/path to the photo to the database". Here are your options, if the picture is not in the server at the time of the switching tabs, implement a loading ui until it has successfully uploaded and the new, updated value, into the database. An easy way to do that would be to use the SVProgressHUD pod and set the default mask type to .clear which disables user interaction. Option 2, is to pass the actual UIImage via a static variable, or in the prepare for segue method, or through a struct, or any other way and set the picture that needs to be refreshed to the uiimage without getting it from the server only when you switch tabs.

Okey, I found the answer. The problem was in initialize UIImage. We have two methods to load image from file.
First one load the image and cache it in memory. Later it use only a reference to this image data in memory:
let image = UIImage(named: savePath)
Second method load image strictly from file every time when user use that function:
let image = UIImage(contentsOfFile: savePath)
Right now it works perfectly :D

Related

SDWebImage doesn't show locally stored image on a real device but it shows up in simulator

I'm using SDWebImages library to get image for thumbnails. It is working seemlesly.
However when I navigate from video to a controller where I play video, I need to show thumbnail once again. I need an image path to pass to the player.
The problem is if I pass the same URL the player will download the image once again. In order to avoid this behaviour i'm trying to get the image from disc which is already stored there by sdwebimages library.
/// get thumbnail from cache
var thumbnail: String?
if (video?.hasThumbnail) {
let urlString = "https://test.com/image/001.png"
if let path = SDImageCache.shared.cachePath(forKey: urlString) {
thumbnail = path
} else {
thumbnail = urlString
}
}
This is working on a simulator, but NOT on the real device.
Please read more about how to use
imageView.sd_setImage(with: URL(string: "http://www.example.com/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))

Image Array Causing memory Issue

Currently creating an basic image slideshow application, To do this i have created an UIImage array and calling them with the following code.
let imageNames = (0...50).map
{
"\($0).JPG"
}
let image = UIImage(named: imageNames[0])
imageView.animationImages = image
imageView.animationDuration = 50
imageView.startAnimating()
Was wondering if anyone would be able to offer some advice.
The issue isn't how much memory the image takes on disk, it's about the memory required to address all the pixels on your screen. You don't really need to store the images before you use them. One image of less than 1MB will load very quickly.
Instead you just need to keep a path to the image, and only when you get to the code that displays the image, then you load it, and display it.
So, combining Alexander's and KKRocks on-point answers:
In the top of your class, define your array of filenames:
let imageNames = (0...55).map { "\($0).JPG" }
Where you are displaying your image:
if let image = UIImage(named: images[0]) {
... the code that is blowing up ...
}
If you are not sure how to save a file and retrieve it, please see this answer I recently gave on that subject:
Saving and Retrieving Files in User's Space.
You need to add image name in array instead of UIImage .
let images: [String] = [
"0.JPG",
"1.JPG")!]
Get image from string of array
let image = UIImage(named: images[0])

Replace UIWebView's (loaded through a web service) <img> tag source with a local (cached) source

I am working on an app (swift) where i need to load a web page inside UIWebView. Inside that UIWebView there's an <img src="http://www.example.com/uploads/43454.jpg" /> element.
All works fine in this scenario, but the problem is that my 43454.jpg image can be of 5-10 megabytes everytime. So when the UIWebView loads it keeps on loading image for about 2 minutes. Plus this <img> tag can have random sources i.e. 22234.jpg, 98734.jpg, 33123.jpg, and so on.
So to tackle this situation I am trying to use following approach:
List all possible images that we need to show in UIWebView, download and cache them (used Kingfisher library for this purpose) at aplication's startup.
When my UIWebView loads my URL initially, it has nothing in it's <img> elements src attribute, but have a data-image-name="22234.jpg" attribute-value pair.
Now when UIWebView finishes loading its contents, get the image name value from data-image-name attribute.
Check for that image in cache, and update the <img> element's src attribute with that image from cache.
This way UIWebView won't be downloading the image over and over again.
Note: Assuming that UIWebView automatically manages resource cache. All other file types *.js, *.css are being properly cached and not being loaded over and over again. The same doesn't go for images, don't know why.
If this approach seems okay, then how should I accomplish it (Swift 2.2)?
Any quick help will be much appreciated. Thanks
This seems to be the same situation in one of my projects. I had exactly this same scenario. I am going to paste my code here, may be it helps you figure out your solution.
As soon as my app loads I create an array of image URLs and pass it to Kingfisher library to download and cache all images (to disk).
for url in response {
let URL = NSURL(string: url as! String)!
self.PlanImagesArray.append(URL)
}
let prefetcher = ImagePrefetcher(
urls: self.PlanImagesArray,
optionsInfo: nil,
progressBlock: {
(skippedResources, failedResources, completedResources) -> () in
print("progress resources are prefetched: \(completedResources)")
print("progress resources are failedResources: \(failedResources)")
print("progress resources are skippedResources: \(skippedResources)")
},
completionHandler: {
(skippedResources, failedResources, completedResources) -> () in
print("These resources are prefetched: \(completedResources)")
print("These resources are failedResources: \(failedResources)")
print("These resources are skippedResources: \(skippedResources)")
self.activityLoadingIndicator.stopAnimating()
})
prefetcher.start()
At my web view screen I initially loaded my web view and after that used following code to check for particular image in cache and if found converted it into a base64 string and put it back in src attribute of the element.
func webViewDidFinishLoad(webView : UIWebView) {
//UIApplication.sharedApplication().networkActivityIndicatorVisible = false
print("webViewDidFinishLoad")
let xlinkHref = webView.stringByEvaluatingJavaScriptFromString("document.getElementById('background_image').getAttribute('xlink:href')")
//print("xlink:href before = \(webView.stringByEvaluatingJavaScriptFromString("document.getElementById('background_image').getAttribute('xlink:href')"))")
if xlinkHref == "" {
let imageCacheKey = webView.stringByEvaluatingJavaScriptFromString("document.getElementById('background_image').getAttribute('data-cache-key')")
let imageCachePath = ImageCache.defaultCache.cachePathForKey(imageCacheKey! as String)
var strBase64:String = ""
webView.stringByEvaluatingJavaScriptFromString(
"var script = document.createElement('script');" +
"script.type = 'text/javascript';" +
"script.text = \"function setAttributeOnFly(elemName, attName, attValue) { " +
"document.getElementById(elemName).setAttribute(attName, attValue);" +
"}\";" +
"document.getElementsByTagName('head')[0].appendChild(script);"
)!
if(imageCachePath != "") {
ImageCache.defaultCache.retrieveImageForKey(imageCacheKey! as String, options: nil) { (imageCacheObject, imageCacheType) -> () in
let imageData:NSData = UIImageJPEGRepresentation(imageCacheObject!, 100)!
strBase64 = "data:image/jpg;base64," + imageData.base64EncodedStringWithOptions(.EncodingEndLineWithCarriageReturn)
webView.stringByEvaluatingJavaScriptFromString("setAttributeOnFly('background_image', 'xlink:href', '\(strBase64)');")!
}
} else {
webView.stringByEvaluatingJavaScriptFromString("setAttributeOnFly('background_image', 'xlink:href', '\(imageCacheKey)');")!
}
}
Hope it helps.
It sounds like you're basically saying "I know what the images are ahead of time, so I don't want to wait for the UIWebView to load them from the server every time. I want the html to use my local copies instead."
While it's a hack, my first thought was:
Have 2 UIWebViews: one is visible to the user and the other is hidden.
"Trigger" the real page in the hidden UIWebView. When the HTML reaches you...
Create a new string, that is a copy of that HTML, but with the image tags that say <img src="http://... replaced with <img src=\"file://... and pointing to the correct image(s) on disk.
Now tell the visible UIWebView to load that HTML string you built in step 3.

how can I get the data of cached images SDWebImage

I'm using SDWebImage library to cache web images in my UICollectionView:
cell.packItemImage.sd_setImage(with: URL(string: smileImageUrl[indexPath.row]))
but I want to save the cached images locally in a file instead of downloading them again
FileManager.default.createFile(atPath: newPath, contents: Data(contentsOf: URL(string: snapchildvalue[Constants.smiles.smileImageUrl] as! String)!), attributes: nil)
is there a way to get the data of cached images
SDWebImage caches downloaded images automatically by default. You can use SDImageCache to retrieve images from the cache. There is a memory cache for the current app session, which will be quicker, and there is the disk cache. Example usage:
if let image = SDImageCache.shared().imageFromDiskCache(forKey: imageURL.absoluteString) {
//use image
}
if let image = SDImageCache.shared().imageFromMemoryCache(forKey: imageURL.absoluteString) {
//use image
}
Also make sure you import SDWebImage in your file. (If you're using Swift/Carthage, it will be import WebImage
SDWebimage chaches image once it is downloaded from a url. Basically it saves image against a url and next time if an image is available for a URL. It will simply get that image from cache. So the below method will be called instantly if the image is already downloaded to device.
imgView.sd_setImage(with: URL(string:url), completed: { (image, error, type, url) in
imgView.image = image
//Do any thing with image here. This will be called instantly after image is downloaded to cache. E.g. if you want to save image (Which is not required for a simple image fetch,
//you can use FileManager.default.createFile(atPath: newPath, contents: UIImagePNGRepresentation(image), attributes: nil)
})
Still if you want to save that image somewhere else or modify it or whatever, you can do it in the completion block above.
SDWebImage already have this kind of caching file locally
Create a SDImageCache with namespace of your choice
Try get the image with imageCache.queryDiskCache
If the image exist, set it to your imageview, if not, use sd_setImage to get the image then save it to the local cache with SDImageCache.shared().store
The key usually to be the image url string
Something like this, might not be correct syntax:
imageCache.queryDiskCache(forKey: urlForImageString().absoluteString, done: {(_ image: UIImage, _ cacheType: SDImageCacheType) -> Void in
if image {
self.imageView.image = image
}
else {
self.imageView.sd_setImage(withURL: urlForImageString(), placeholderImage: UIImage(named: "placeholder")!, completed: {(_ image: UIImage, _ error: Error, _ cacheType: SDImageCacheType, _ imageURL: URL) -> Void in
SDImageCache.shared().store(image, forKey: urlForImageString().absoluteString)
})
}
})

Swift - Create a GIF from Images and turn it into NSData

This might be an amateur question, but although I have searched Stack Overflow extensibly, I haven't been able to get an answer for my specific problem.
I was successful in creating a GIF file from an array of images by following a Github example:
func createGIF(with images: [NSImage], name: NSURL, loopCount: Int = 0, frameDelay: Double) {
let destinationURL = name
let destinationGIF = CGImageDestinationCreateWithURL(destinationURL, kUTTypeGIF, images.count, nil)!
// This dictionary controls the delay between frames
// If you don't specify this, CGImage will apply a default delay
let properties = [
(kCGImagePropertyGIFDictionary as String): [(kCGImagePropertyGIFDelayTime as String): frameDelay]
]
for img in images {
// Convert an NSImage to CGImage, fitting within the specified rect
let cgImage = img.CGImageForProposedRect(nil, context: nil, hints: nil)!
// Add the frame to the GIF image
CGImageDestinationAddImage(destinationGIF, cgImage, properties)
}
// Write the GIF file to disk
CGImageDestinationFinalize(destinationGIF)
}
Now, I would like to turn the actual GIF into NSData so I can upload it to Firebase, and be able to retrieve it on another device.
To achieve my goal, I have two options: Either to find how to use the code above to extract the GIF created (which seems to directly be created when creating the file), or to use the images on the function's parameters to create a new GIF but keep it on NSData format.
Does anybody have any ideas on how to do this?
Since nobody went ahead for over six months I will just put the answer from #Sachin Vas' comment here:
You can get the data using NSData(contentsOf: URL)

Resources