Gif using UIImage+animtedGif class - ios

Using this class I am trying to load a gif url into UIImageView.
The thing is , for some url's it takes 10 seconds to load, others 2 seconds.
I have tried almost anything, but still the process is too slow. 1 second would be good, but i had never succeed getting there.
I have also tried with UIWebview which had its own issues .
Here is the code :
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let fileUrl = NSURL(string:"http://45.media.tumblr.com/6785bae27b8f888fe825f0ade95796a3/tumblr_noenkbeTSw1qjmwryo1_500.gif" )
let gif = UIImage.animatedImageWithAnimatedGIFURL(fileUrl!)
dispatch_async(dispatch_get_main_queue()) {
self.player.image = gif
}
}

The problem with most of the GIF reading tools I have looked at is that they read all the data in at load time and that they allocate memory for all of the decoded frames and hold all that uncompressed data in memory at the same time. This will lead to runtime performance problems and it will crash your app and possibly your device on large/long gifs. On the issue of loading time, there is not all that much you can do since the data does need to be downloaded and read. You are also just assuming that the network cache is going to handle hitting the same GIF over and over without going to the network again, which may or may not work well for you. For a solution that addresses these issues, see this SO Question or you can also take a look at the flipboard solution here.

Related

AKTableView issue 100% CPU usage

I'm trying to assign a table view to an already existing UIView object on my storyboard but every time I assign it by specifying the correct frame I get an issue.
let sampleTable = AKTableView(AKTable(file: sampleReferences[indexPath.row].sampleFile!), frame: samplePlot.bounds)
samplePlot.addSubview(sampleTable)
This is the code I'm using at the moment. I've also used samplePlot.frame.
Basically the sampleReferences[indexPath.row].sampleFile! is an AKAudioFile stored earlier from a recording stored as a reference from a UITableView element. It's quite a large program split into multiple files so it's hard to show everything.
The only extra issue I can think of is I haven't stopped any of the current AudioKit processes. I'm not sure if the table can only be drawn at startup.
The following works with a .sine specifier AKTable(.sine) so it's not an issue with my UI code. It also fails when trying to load even a basic .mp3 file from .resources.
Update:
I've compressed the test .mp3 file significatly and shortened it's overall length to 2 seconds and I'm still getting the same issue. I think it might be an issue with the actual function used to draw the file. Although I could be wrong.
Another update:
So everything works fine using the following code
let file = EZAudioFile(url: sampleAudio!.url)
guard let data = file?.getWaveformData() else {
print("Failed to get waveform data")
return }
let waveform = EZAudioPlot()
waveform.frame = samplePlot.bounds
waveform.plotType = EZPlotType.buffer
waveform.shouldFill = true
waveform.shouldMirror = true
waveform.color = UIColor.black
waveform.updateBuffer(data.buffers[0], withBufferSize: data.bufferSize )
samplePlot.addSubview(waveform)
but obviously I'm using EZAudioPlot. I grabbed this from a previous stack overflow issue for the same thing and edited it a bit.

WebP encodedData loads for 30+ seconds

iOS version: 13.1
iPhone: X
I'm currently using DBAttachmentPickerController to choose from a variety of images, the problem comes when I take a picture directly from the camera and try to upload it to our server. The SDImageWebPCoder.shared.encodedData loads for about 30 seconds more less. The same image in the Android app takes about 2-3 seconds.
Here is the code I use
let attachmentPickerController = DBAttachmentPickerController(finishPicking: { attachmentArray in
self.images = attachmentArray
var currrentImage = UIImage()
self.images[0].loadOriginalImage(completion: { image in
self.userImage.image = image
currrentImage = image!
})
//We transform it to webP
let webpData = SDImageWebPCoder.shared.encodedData(with: currrentImage, format: .webP, options: nil)
self.api.editImageUser(data: webpData!)
}, cancel: nil)
attachmentPickerController.mediaType = DBAttachmentMediaType.image
attachmentPickerController.allowsSelectionFromOtherApps = true
attachmentPickerController.present(on: self)
Should I change the Pod I'm using? Should I just compress it? Or am I doing something wrong?
WebP encoding speed is related slow, it use software encoding and VP8 compression algorithm (complicated), compared to the Hardware accelerated JPEG/PNG encoding. (Apple's SoC).
picture directly from the camera
The original image taken on iPhone camera may be really lark, like 4K resolution. If you don't do some pre-scale and try to encode it, you may consume much more time.
The suggestion can be like this:
Try to use the options like the compressionQuality, the higher cost
more time, but compress more.By default it's 1.0, which is the higest and most time consuming.
Try to pre-scale the original image. For image from Photos Libraray, you can always use the API to control the size. Or, you can use SDWebImage's transform method like - [UIImage sd_resizedImage:].
Do all the encoding in background thread, never block main thread
If all these is not suitable, the better solution, it's to use JPEG and PNG format instead of WebP. Then, on your image server side code, transcoding the JPEG/PNG to WebP. Server side processing is always the best idea for this thing.
If you're intersted the real benchmark or something, compared to JPEG/PNG (Hardware) and WebP (Software). You can try to use my benchmark code demo here, to help you do your decision.
https://github.com/dreampiggy/ModernImageFormatBenchmark

Delete File After contentsOfFile Load

I am loading an array of UIImage from the iOS Documents directory:
var images = [UIImage]()
for fileName in fileNames {
images.append(UIImage(contentsOfFile: "\(imagesPath)/\(fileName).png")!)
}
I'm going to continue using this array but I don't need the files anymore, so I go ahead and delete them:
for fileName in fileNames {
do {
try NSFileManager.defaultManager().removeItemAtPath("\(imagesPath)/\(fileName).png")
} catch {
print("Error")
}
}
When I do this, my array of UIImage is now invalid and gives me errors while trying to access them. Shouldn't this be in memory and not related to the files on disk?
I tried using the ".copy()" command on the images when I load them but that made no difference.
I have confirmed that the delete is the issue above because if I comment out that line the app works great with no errors. I only get errors accessing the array after I delete the files from disk.
Is there a way to sever this connection?
Edit: Per the correct answer from #Wain, the code works fine if I change it to:
var images = [UIImage]()
for fileName in fileNames {
let imgData = NSFileManager.defaultManager().contentsAtPath("\(imagesPath)/\(fileName).png")!
images.append(UIImage(data: imgData)!)
}
Doing this doesn't keep the link back to the file on disk.
The images haven't been displayed so the data hasn't fully been loaded yet, only enough to know the image details and size has been loaded. This is for memory efficiency. depending on the image format and usage different parts of data may be loaded at multiple different times.
If you load the file into NSData, ensuring that it isn't memory mapped, and create the image from that then the data and image should be unlinked from the underlying file. This is less memory efficient and it would be better to keep your current code and delete the files when you know you're finished with the images all together.

Determine memory limit of iOS today extension

I'm developing an iOS today extension, that can read an image from UIPasteboard and save it on disk. This process fails with large images because iOS extensions can't use much memory. To workaround this issue, I'm checking the size of the image first and try to decide, if the widget can save it or should delegate this task to its host app:
let MAXIMUM_IMAGE_SIZE_BYTES = <SomeMagicNumber>
if let clipboardImage = UIPasteboard.generalPasteboard().image {
let imageSize = CGImageGetHeight(clipboardImage.CGImage) * CGImageGetBytesPerRow(clipboardImage.CGImage)
if imageSize > MAXIMUM_IMAGE_SIZE_BYTES {
// Open host app to save image
}
else {
// Save image directly
}
}
I have the following questions:
Is my size calculation correct? I took it from this thread. I cannot instantiate a JPEG or PNG representation and read its size because of the memory limitations mentioned above.
Can I get rid of that magic number for the maximum image size in bytes? If not, are there any official specifications from Apple that I can use? I cannot test my app on every available iOS model and don't want to risk crashes on older devices.
Thanks a lot for your help!
I'm just starting to look at the memory that a notification service extension is using. I found this presentation. Might be helpful for others.
https://cocoaheads.tv/memory-use-in-extensions-by-conrad-kramer/
What was your solution to this issue?

ios fast Image cache with server

I am trying to use Path's FastImageCache library to handle photos in my app. The sample they provide simply reads the images from disk. Does anyone know how I might modify it to read from a url? In the section about providing source images to the cache they have
- (void)imageCache:(FICImageCache *)imageCache wantsSourceImageForEntity:(id<FICEntity>)entity withFormatName:(NSString *)formatName completionBlock:(FICImageRequestCompletionBlock)completionBlock {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Fetch the desired source image by making a network request
NSURL *requestURL = [entity sourceImageURLWithFormatName:formatName];
UIImage *sourceImage = [self _sourceImageForURL:requestURL];
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(sourceImage);
});
});
}
Has anyone used this api before and know how to get the source from the server to pass to the cache? Another example that still uses hard disk is
- (void)imageCache:(FICImageCache *)imageCache wantsSourceImageForEntity:(id<FICEntity>)entity withFormatName:(NSString *)formatName completionBlock:(FICImageRequestCompletionBlock)completionBlock {
// Images typically come from the Internet rather than from the app bundle directly, so this would be the place to fire off a network request to download the image.
// For the purposes of this demo app, we'll just access images stored locally on disk.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *sourceImage = [(FICDPhoto *)entity sourceImage];
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(sourceImage);
});
});
}
I worked on Fast Image Cache while I was at Path. The critical portion of Fast Image Cache is that it is the absolute fastest way to go from image data on disk to being rendered by Core Animation. No decoding happens, none of the image data is kept in memory by your app, and no image copies occur.
That said, the responsibility is yours to figure out how to download the images. There's nothing inherently special about downloading images. You can use NSURLConnection or one of many popular networking libraries (like AFNetworking) to actually download the image data from your server. Once you have that image data, you can call the relevant completion block for Fast Image Cache to have it optimize it for future rendering.
If you're looking for a simple way to download an image and display it when it's finished, then use something like SDWebImage. It's great for simple cases like that. If you are running into performance bottlenecks—especially with scrolling—as a result of your app needing to display tons of images quickly, then Fast Image Cache is perfect for you.
Your Approach Seems a Lot Like Lazy Loading Images from the URL, I had to do this once I had Used the following Library to do it, It dosent stores the Images in the disk, but uses cached Images..the below is its link..
https://github.com/nicklockwood/AsyncImageView
I added the networking logic to our fork > https://github.com/DZNS/FastImageCache#dezine-zync-additions-to-the-class
It utilizes NSURLSessionDownloadTasks, has a couple of configuration options (optional). All you need to do is create a new instance of DZFICNetworkController and set it as the delegate for FICImageCache's sharedCache instance object. It'll take care of downloading images with reference to the sourceImageURLWithFormatName: method on your objects conforming to <FICEntity>.
As I assume you'd use this in a UITableView or UICollectionView, calling cancelImageRetrievalForEntity:withFormatName: on the imageCache will cancel the download operation (if it's still in-flight or hasn't started).

Resources