I want to show images like https://www.pinterest.com in collectionView using swift 3. For that the image is coming from backend with just URL not the image sizes. So i want to dynamically show cell according to height of images.
What i am doing is,
let url = self.myArray[indexPath.row].image
let data = NSData(contentsOf:URL(string: url)!)
var photo = UIImage()
if (data?.length)! > 0 {
photo = UIImage(data:data! as Data)!
}
let boundingRect = CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT))
let rect = AVMakeRect(aspectRatio: photo.size, insideRect: boundingRect)
return rect.size.height
It return me the height.
Problem is: Images are HD with upto 3MB size.
let data = NSData(contentsOf:URL(string: url)!)
takes so much time, as i have 20 to 30 images.
Is there any way to download the image on another thread instead of main thread so downloading image will continue in background,
OR anyone have a better solution for calculating height of images from URL.
PS: I have followed https://www.raywenderlich.com/107439/uicollectionview-custom-layout-tutorial-pinterest for customLayout.
Thanks in advance.
You should look into Alamofire. It uses promises to make the requests asynchronously without holding up the UI thread. Your code would look something like this
Alamofire.request(URL_STRING, method: .get, parameters: nil, encoding: URLEncoding.default, headers: getHeaders())
.validate()
.responseImage(completionHandler: {response in
switch response.result {
case .success:
//The image will be stored in response.data in NSData format
break
case .failure(let error):
//Handle errors over here
break
}
})
Related
I'm working on the offline downloads section for a video streaming app. Currently, I'm able to download videos and also can see the file path for the downloads as follows:
Button(action: {
let fileManager = FileManager.default
do {
let resourceKeys : [URLResourceKey] = [.creationDateKey, .isDirectoryKey]
let documentsURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let enumerator = FileManager.default.enumerator(at: documentsURL,
includingPropertiesForKeys: resourceKeys,
options: [.skipsHiddenFiles], errorHandler: { (url, error) -> Bool in
print("directoryEnumerator error at \(url): ", error)
return true
})!
for case let fileURL as URL in enumerator {
let resourceValues = try fileURL.resourceValues(forKeys: Set(resourceKeys))
// print(fileURL.path, resourceValues.creationDate!, resourceValues.isDirectory!)
print(fileURL.path)
self.array.append(fileURL.path)
}
} catch {
print(error)
}
}){
Text("Check downloads")
}
//response when button is clicked
/private/var/mobile/Containers/Data/Application/AD90B554-3465-43DD-BAE2-04BC83F850A3/Documents/Bz75srG5nctDCnAWIspM.mp4
/private/var/mobile/Containers/Data/Application/AD90B554-3465-43DD-BAE2-04BC83F850A3/Documents/GWYgrgDVYaowxeGcOjzp.mp4
Since I've renamed the mp4 files using movie ID's (eg: GWYgrgDVYaowxeGcOjzp.mp4) my goal is to use this name to show which videos were downloaded and allow users to play the video. I'm able to do the name processing later on, but how am I able to get the downloaded mp4 files into a view like a VStack? I was thinking of looping through and adding the files into an array but wanted to check if there's a different way. Also, would be great if you could show me how to play a video using the path as the url. I've tried applying suggested methods but ended up with a "NSURLConnection finished with error - code -1002" error. Thank you!
I am downloading images from my server using this
let queue = DispatchQueue.global(qos: .userInteractive)
queue.async{ ...
And while images downloading user can tap the button. But sometimes this button doesn't work (when all images done button work correct).
How to solve this problem?
Donot access global queue for that, instead create a custom DispatchQueue with label, qos as .background and fetch the image, when done use DispatchQueue.main to return the image to the main queue
For e.g:
let queue = DispatchQueue(label: "myImageQueue", qos: .background)
queue.async {
...fetch image
DispatchQueue.main.async {
self.imageView.image = fetchedImage
}
}
i am trying to create a toolbar with buttons. and the button i want to have is an image rather title. The current non working code is:
let imageName = "yourImage.png"
self.myUIBarButtonBack = UIBarButtonItem(image: UIImage(named: imageName), style:.plain, target: self, action: #selector(onClickBarButton))
I have 2 questions:
1. where should i place the yourImage.png in my application
2. is this code sufficient to render image or i need to do things like putting it into imageView component and make it visible etc. ?
The best approach is to add images in xcassets. This is the best way you can organize images. The concept of App slicing applies here.
You don't need to put the image in image view in the case of bar button item.
Try changing the rendring option as Original Image instead of Default.
One way is create custom button and assign to toolbar like navigationbar
self.navigationItem.hidesBackButton = true
let button = UIButton.init(type: .custom)
button.setImage(UIImage.init(named: "back_icon"), for: UIControlState.normal)
button.addTarget(self, action:#selector(onClickBackBarItem), for: UIControlEvents.touchUpInside)
button.frame = CGRect.init(x: 0, y: 0, width: 25, height: 25)
let barButton = UIBarButtonItem.init(customView: button)
self.navigationItem.leftBarButtonItem = barButton
I'm trying to test if an image has been set by an imageView extension. I have the test passing when trying to load in from an http url. Now I want to test if the image gets set from an https url.
My current code is:
func test_DateImageLoadedFromHTTPSURL() {
let expected = expectation(description: "Image from https did load")
let viewer = UIImageView(frame: CGRect(x: 0, y: 0, width: 300, height: 250))
viewer.imageFromServerURL(urlString: "https://dummyimage.com/300x250/000/fff.png")
if viewer.image != nil {
expected.fulfill()
} else {
XCTFail()
}
waitForExpectations(timeout: 3.0, handler: nil)
}
It should work, unless I'm not seeing it.
Thanks
The order that you are doing things here will never work. Your method is asynchronous here so the order of things happening will be something like...
Run the method to get the image
Check the imageView.image is not nil (it is still nil at this point)
Wait for expectations
Download of image finished
Set the image
The method is asynchronous so the order of things happening changes.
You need to add some sort of completion to your method so that you can check the image is not nil AFTER it has been set and the completion been called.
Like...
viewer.imageFromServerURL(urlString: "https://dummyimage.com/300x250/000/fff.png") {
// image has finished loading here...
// check the image is not nil
}
Of course, you will need to update the actual method so that it will accept a closure and run the closure once the image is loaded.
I have the following jsfiddle which he an existing image with a button that when clicked will change the image src randomly from an array.
JSFIDDLE: http://jsfiddle.net/GpSbd/7/
Some of these images will be very large, is it possible to reduce or compress the image file down before it does setImage and draws it onto the stage as i will eventually be uploading the images onto our server so want them to be as small as possible
// CHANGE IMAGE FUNCTION
$('#changeImage').on("click", function(){
var newImage = new Image();
var img = layer.get('#Image1')[0];
newImage.onload = function() {
<-- CAN I COMPRESS IT AT THIS STAGE? -->
img.setImage(newImage);
layer.draw();
};
var random = pictures[Math.floor(Math.random()*pictures.length)];
newImage.src = random;
});
You already have the image urls saved in your pictures[] array.
Just save that url (random) on your server and not the image itself.
This cuts your storage size by 20-100 times.
If the server needs to temporarily use the image, the server can use the url to fetch it.