Work with multi-diminutional array dynamically as UIImage array - ios

I'm working on app to do paged photos inside scrollViews like in the photos app to swipe right to get the old photos and swipe left to get the new photos until the end of photos.
Alright,so i am getting the photos as a multi-diminutional from the web :
imageArray[indexPath.row][0] as? String
Thats in the ViewController1 to show all the images in CollectionView .
When the user press on a photo i do segue and show the image larger in ViewController2 so if swipe left it show the new photos and right to show the old photos which is stored in the array.
but i need to covert my two-dimensional array to one and use it dynamically to be something like this :
pageImages = [UIImage(named:"photo1.png")!,
UIImage(named:"photo2.png")!,
UIImage(named:"photo3.png")!,
UIImage(named:"photo4.png")!,
UIImage(named:"photo5.png")!]
how is it possible to do ?
i could say like :
pageImages = [UIimage(named:thewholearray)] ?
i tried first to convert to one-diminutional array but i failed :
var imageArray : NSArray = []
var mybigarray : NSArray = []
for (var i=0; i<=self.imageArray.count; i++) {
self.mybigarray = self.imageArray[i][0] as! NSArray
}
which generate this casting error :
Could not cast value of type '__NSCFString' (0x196806958) to 'NSArray' (0x196807308).

You can use the map-function to extract the image names from your multi-dimensional array.
var imageNames:[String] = imageArray.map({
$0[0] as! String
})
The map function iterates through all array entries like a for-in-loop.
The statement in the closure determines the new entry of your imageNames array.
EDIT:
If you don't use Swift-Arrays:
var imageNames:NSMutableArray = []
for image in imageArray{
imageNames.addObject(image[0] as! String)
}

I looked at the tutorial and I think this will help with the answer from Daniel. Keep Daniel's Answer and use the URLs from the array with the extension.
Add this to create you images from a URL
extension UIImageView {
public func imageFromUrl(urlString: String) {
if let url = NSURL(string: urlString) {
let request = NSURLRequest(URL: url)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
if data != nil {
self.image = UIImage(data: data)
}
else {
self.image = UIImage()
}
}
}
}
}
Use this in the PagedImageScrollViewController:
newPageView.imageFromUrl(pageImages[page])
pageImages is your array of Strings.

Related

Retrieve multiple images from Parse with getdatainbackground (IOS - Swift)

I try to do a query with Parse containing a few Strings and Images that will be added to an array. The strings in the array are all in the right order but not the images. I think its probably because some images are smaller than the other ones and so they get appended to the array earlier than they are supposed to. Is there any way to "save" space in the array for the images to keep them in the right order? It's probably not that hard to solve that but I am a Newbie :( Thank you!
query.findObjectsInBackground (block: { (objects:[PFObject]?, error: Error?) -> Void in
for object in objects! {
DispatchQueue.global(qos: .userInteractive).async {
// Async background process
if let imageFile : PFFile = self.bild.append(object.value(forKey: "Bild") as! PFFile) {
imageFile.getDataInBackground(block: { (data, error) in
if error == nil {
DispatchQueue.main.async {
// Async main thread
let image = UIImage(data: data!)
image2.append(image!)
}
} else {
print(error!.localizedDescription)
}
})
}
}
}
})
Your analysis is correct that the requests will complete in non-deterministic order, partly or mostly influenced by the amount of data that must be returned.
Instead of an array to which you append the UIImage (or data), use a mutable dictionary that maps strings to UIImage. A reasonable choice for the string key is the PFFile name.
EDIT I'm not a Swift writer, but I tried to express the idea below (don't depend on it compiling, but I think the idea is sound)
class MyClass {
var objects: [PFObject] = []
var images: [String: UIImage] = [:] // we'll map names to images
fetchObjects() {
// form the query
query.findObjectsInBackground (block: { (objects:[PFObject]?, error: Error?) -> Void in
self.objects = objects
self.fetchImages()
})
}
fetchImages() {
for object in self.objects! {
if let imageFile : PFFile = object["Bild"] as PFFile {
self.fetchImage(imageFile);
}
}
}
fetchImage(imageFile: PFFile) {
imageFile.getDataInBackground(block: { (data, error) in
if error == nil {
self.images[imageFile.name] = UIImage(data: data!)
// we can do more here: update the UI that with image that has arrived
// determine if we're done by comparing the count of images to the count of objects
} else {
// handle error
}
}
}
}
This will get the images in the background and keep them associated with their filenames using a dictionary. The OP code didn't explain what self.bild is, but I assumed it was an instance array of retrieved PFFiles. I replaced this with the images instance var.
Image file order is maintained by the object collection: to get the Nth image, get the Nth object, get it's "Bild" property, that PFFile's name is the key into your images dictionary.
var n = // some index into objects
var object : PFObject = self.objects[n]
var file : PFFile = object["Bild"]
var name : String = file.name
var nthImage = self.images[name] // is nil before fetch is complete

download images using alamofire - iOS

I'm using Alamofire to fetch data from server and then put them in an array of CarType objects which CarType is my struct. what I get from server is name , id and iconUrl. from iconUrls i want to download icons and put them in icon. after that I'll use icon and name in a collection view. my Alamofire request is:
var info = [CarType]()
Alamofire.request(.GET,"url")
.responseJSON { response in
for (_,subJson):(String, JSON) in json["result"]
{
let name = subJson["name"].string
let iconUrl = subJson["icon"].string
let id = subJson["id"].int
info.append(CarType(id: id!, name: name!, iconUrl: iconUrl! , image: UIImage()))
}
my struct is:
import Foundation
import UIKit
struct CarType {
var name : String
var id : Int
var iconUrl : String
var icon : UIImage
}
I want to download images before using them in collectionView.
How can i download images (using AlamofireImage) and put them in related carType icon property?
What you are asking is really bad practice in mobile app. Just a case, for example, you made a request and got like 20 items in an array, and in order to put all UIImages in your models, you have to make 20 more requests, also you even don't know if your users will eventually use (view) those icons or no.
Instead, you could fetch the images when the cell (I guess, you will be displaying those icons in a cell) is displayed, for this purpose you could use libs like SDWebImage(objective c) or Kingfisher(swift) which have extensions for UIImageView to make it simple to fetch and display image on. Those libs can also cache the downloaded image.
Also, another suggestion for object mapping. Currently, you are mapping json to your model manually. There are a lot of good libs to handle that for you, which could automate your object mapping process, for example - ObjectMapper
Hope, this was helpful. Good Luck!
I have done functionalities thing in UITableview, added following in CellForRowIndex method:
getDataFromUrl(urlString){(data,response,error) -> Void in
if error == nil {
// Convert the downloaded data in to a UIImage object
let image = UIImage(data: data!)
// Store the image in to our cache
if((image) != nil){
// Store the image in to our cache
self.imageCacheProfile[urlString] = image
// Update the cell
DispatchQueue.main.async(execute: {
cell.imgvwProfile?.image = image
})
}
else{
cell.imgvwProfile!.image = UIImage(named: "user")
}
}
}
func getDataFromUrl(_ strUrl:String, completion: #escaping ((_ data: Data?, _ response: URLResponse?, _ error: NSError? ) -> Void)) {
let url:URL = URL(string: strUrl)!
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {data, response, err in
print("Entered the completionHandler")
}.resume()
}
You also need to declare imageCache to store downloaded images.
var imageCache = [String:UIImage]()
You can use above code in your method and it should work just.

Swift array: how to append element references in order to update element later on from a synch request?

[UPDATE]
I have added actual code snippet in order to make my question clear.
Say we want to store uiimages into an array, which are fetched from the internet.
I have this code snippet:
// Somewhere in a loop
{
var story = Story()
story.imgUrl = "http:\(imgUrl)"
/// Donwload image, and replace in the top
if let imgUrl = story.imgUrl {
if let url = NSURL(string: imgUrl) {
let request = NSURLRequest(URL: url)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
(response, data, error) -> Void in
if let data = data {
story.image = UIImage(data: data)
var i = 0
for a in self.Stories {
print("iv image \(i++) is \(a.image)")
}
print("Received image for story as \(story.image) into story \(story)")
// Should one set the image view?
if let _ = self.imageView {
if let indexPath = self.tableView?.indexPathForSelectedRow {
if stories.count == indexPath.section { // is that the currently selected section?
self.imageView?.image = story.image
self.imageView?.hidden = false
print("Set imageView withstory \(story.tag.string)")
}
}
}
}
}
}
}
stories.append(story)
}
/// Switch data source
self.Stories = stories
This doesn't store the image property value into the destination array.
Though the image is ok in the block, if I iterate through the destination array, my image is nil
image: Optional(<UIImage: 0x7ff14b6e4b20> size {100, 67} orientation 0 scale 1.000000))
iv image 0 is nil
How to achieve above functionality?
[INITIAL QUESTION]
Say we want to store element i.e UIImages which i have fetched from the internet. I have this code snippet:
var array = []
let imageView = UIImageView(image:nil)
array.append(imageView)
// and later or, in an synch block
imageView.image = image
This doesn't store the image property value in the array.
How could I do that?
Gotcha!
The point is that Story was defined as struct. And structs are passed by values unlike classes.
To make the code working, I just changed from struct Story {} to class Story {}, and voila!

Test if PFFile array is empty

I have an array that starts out empty but is filled with PFFiles (image data) with a PFQuery. A UIImageView has its image set using the data in the PFFile Array. However, if the array is empty then there is an error saying array index is out of range. Therefore, I need something testing to see if array is empty and I can't find a way to do that.
var imageFiles = [PFFile]()
And then in the viewDidLoad
self.imageFiles[self.imageCounter].getDataInBackgroundWithBlock{
(imageData, error) -> Void in
if error == nil {
let image = UIImage(data: imageData!)
self.mainPic.image = image
}else {
}
}
I would like to be able do something like:
If let testVariable = self.imageFiles[self.imageCounter] as PFFile {
}
Or more simply:
If self.imagesFiles[self.imageCounter] == nil {
}
But neither of those work
The problem is you're going out of the bounds of the array, as you said. You can void this by checking the size of the array before trying to access an element. The following should do the trick:
if (self.imagesFiles.count > self.imageCounter) {
//myImage = self.imageFiles[self.imageCounter];
}

Loading image in Swift from Parse

I'm successfully pulling in data from Parse into swift, but my images don't seem to be working the way I'm doing it.
In my cellForRowAtIndexPath method, I do the following:
var event: AnyObject? = eventContainerArray[indexPath.row]
if let unwrappedEvent: AnyObject = event {
let eventTitle = unwrappedEvent["title"] as? String
let eventDate = unwrappedEvent["date"] as? String
let eventDescription = unwrappedEvent["description"] as String
let eventImage = unwrappedEvent["image"] as? UIImage
println(eventImage)
if (eventImage != nil){
cell.loadItem(date: eventDate!, title: eventTitle!, description: eventDescription, image: eventImage!)}
else {
let testImage: UIImage = UIImage(named: "test-image.png")!
cell.loadItem(date: eventDate!, title: eventTitle!, description: eventDescription, image: testImage )
}
}
}
return cell
}
I'm using println() with my PFQuery, and I am seeing this as part of the object that's loading in: image = "<PFFile: 0x7fee62420b00>";
So I'm getting title, date, description, etc. all loading fine as part of the above eventContainerArray, but when I look at eventImage, it's nil every time. In the code above, it always defaults to loading test-image.png, being that the image is coming up nil. Am I simply handling that PFFile the improper way? Not sure why it's not working.
Thanks!
Your image is likely a PFFile object. You will need to load it to get an UIImage object from it.
let userImageFile = unwrappedEvent["image"] as PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData!, error: NSError!) -> Void in
if error == nil {
let eventImage = UIImage(data:imageData)
// do something with image here
}
}
I'm not familiar with Parse, but given what you're seeing from the println() it looks like what you've received isn't a UIImage but instead some sort of data type. This makes sense; you aren't going to be receiving ObjC objects over the network. So you are getting a file, but when you use the conditional downcast (x as? UIImage) it's returning nil because you don't have a UIImage.
Instead you're going to need to cast to PFFile, and then probably create a UIImage with UIImage(data: file.getData) or something similar. I'm not familiar with exactly how Parse works.
Edit: here's a related question that might be helpful: I can't get image from PFFile

Resources