Well I have been making a test app to continue my swift learning, today I came across a problem.
Basically I have a tableview and a detailview. For my tableview file I have some data that I am currently passing to the detailview, like the name that goes on the navigation bar, one image and some text, this data is stored in arrays on my tableview file, I use "prepareforsegue" to pass this information:
var names = ["name1","name2","name3"]
detailViewController.detailName = names[indexPath.row]
Then in my detailViewController I have variables set for that:
var detailName: String?
Then I use this for stuff, example: naming my navigation bar or setting an image in detailView:
navigationItem.title = detailName!
Now, what I dont get how to do is pass a whole array of information to a variable in my detailViewController. What I want to do is pass an array of images to use it on my detailView. I want to be able to iterate through the array of images with a button, I know how to set that up but I just need to know how to pass the array, right now I am just passing one of the values(one name, one image etc...)
Thanks for the help in advance.
It's not so different from what you have done.
Just add field for images in detailViewController, and then pass images using it. Images could be represented in [UIImage]. However, [String] can be also used for local filenames, and [NSURL] can be used for remote image urls.
code:
In DetailViewController:
var images: [UIImage]? // or var images: [UIImage] = []
In prepareForSegue:
detailViewController.images = YourImages
You seem to be asking for one of two things:
A UIImage array, which you can declare using var imageArray : [UIImage] = [] and then append whatever images you want to pass.
Alternatively, you can pass in a [AnyObject] array and cast its elements to a UIImage or String by doing
var objectArray : [AnyObject] = []
objectArray.append("test")
objectArray.append(UIImage(named: "test.png")!)
if let s = objectArray[0] as? String {
// Do something with the string
}
if let i = objectArray[1] as? UIImage {
// Do something with the image
}
if let s = objectArray[1] as? String {
// The above cast fails, this code block won't be executed
} else {
// ... but this one will
}
in the table view controller file:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get reference to the destination view controller
var detailVC = segue.destinationViewController as! DetailViewController
var detailImages: Array<UIImage> = []
detailImages.append(UIImage(named: "pup.png")!)
detailImages.append(UIImage(named: "cutepuppy.png")!)
detailVC.detailImages = detailImages;
}
and in the detail file:
var detailImages: Array<UIImage>?
Related
I am currently struggling with obtaining a value from an array inside an array of dictionaries. Basically I want to grab the first "[0]" from an array stored inside an array of dictionaries. This is basically what I have:
var array = [[String:Any]]()
var hobbies:[String] = []
var dict = [String:Any]()
viewDidLoad Code:
dict["Name"] = "Andreas"
hobbies.append("Football", "Programming")
dict["Hobbies"] = hobbies
array.append(dict)
/// - However, I can only display the name, with the following code:
var name = array[0]["Name"] as! String
But I want to be able to display the first value in the array stored with the name, as well. How is this possible?
And yes; I know there's other options for this approach, but these values are coming from Firebase (child paths) - but I just need to find a way to display the array inside the array of dictionaries.
Thanks in advance.
If you know "Hobbies" is a valid key and its dictionary value is an array of String, then you can directly access the first item in that array with:
let hobby = (array[0]["Hobbies"] as! [String])[0]
but this will crash if "Hobbies" isn't a valid key or if the value isn't [String].
A safer way to access the array would be:
if let hobbies = array[0]["Hobbies"] as? [String] {
print(hobbies[0])
}
If you use a model class/struct things get easier
Given this model struct
struct Person {
let name: String
var hobbies: [String]
}
And this dictionary
var persons = [String:Person]()
This is how you put a person into the dictionary
let andreas = Person(name: "Andreas", hobbies: ["Football", "Programming"])
persons[andreas.name] = Andreas
And this is how you do retrieve it
let aPerson = persons["Andreas"]
I want to pass an array of strings through segue, but my app crashes. Here is what I have in the starting ViewController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "sendToOther") {
let svc = segue.destinationViewController as! OtherMinerals;
svc.toPass1 = String(DataSet[0])
svc.toPass2 = String(DataSet[1])
and this is what I have in the receiving ViewController
var toPass:String!
var toPass2:String!
so, I am passing every item separately through segue, but that's not elegant. I'd like to pass the whole array through, but for some reason I can't get the code right. Any genius out there to assist??
Just create a [DataSet] variable in your second ViewController and pass your whole DataSet array to your second ViewController instead of the two strings:
//second viewcontroller:
var dataSetArray:[DataSet]
//first viewcontroller:
svc.dataSetArray = yourDataSet
Change the type of the variable "toPass" to be [String]. If your DataSet is of the same type you can pass the array like so
svc.toPass = DataSet
There are several posts on SO like this, and the only solution suggested that would seem to work is manually removing and inserting a property at the same index.
But this feels messy, and some posts suggest it's possible in Xcode 7 to directly update dictionary properties if inside an array of dictionaries.
However, it's not working for the code below, generating the Cannot assign to immutable expression of type [String:AnyObject] error.
// Class vars
var userDict = [String:AnyObject]()
var accounts = [[String:AnyObject]]()
func setHistory(index: Int, history: [String]) {
(userDict["accounts"] as! [[String:AnyObject]])[index]["history"]! = history
(userDict["accounts"] as! [[String:AnyObject]])[index]["history"] = history
userDict["accounts"][index]["history"] = history
userDict["accounts"][index]["history"]! = history
}
All four lines inside of setHistory try to do the same thing, and all fail.
Right now the way you are doing this:
userDict["accounts"] as! [[String:AnyObject]])[index]["history"]
you are working with an immutable container.
You are going to have to design it like this:
func setHistory(index: Int, history: [String]) {
//this line copies from user dict, it is not a pointer
var account = userDict["accounts"] as! [[String:AnyObject]];
//this line sets the new history
account[index]["history"] = history;
//this line will update the dictionary with the new data
userDict["accounts"] = account
}
I think you are better off with a class to model your data.
Anyhow, you can call an old friend from ObjC, NSMutableDictionary:
var userDict = [String: AnyObject]()
var accounts = [NSMutableDictionary]()
accounts.append(["history": ["history1.1", "history1.2"]])
accounts.append(["history": ["history2.1", "history2.2"]])
userDict["accounts"] = accounts
func setHistory(index: Int, history: [String]) {
userDict["accounts"]![index].setObject(history, forKey: "history")
}
setHistory(0, history: ["history1.1", "history1.2", "history1.3"])
print(userDict)
I want to be able to save an array, cardImages, that contains UIImages via SwiftyUserDefaults.
Desired Behavior
Here is the exact desired behavior:
Save an array of UIImages to NSUserDefaults via the SwiftyUserDefault library
Retrieve the images later
Code This is stripped down to very little code
var newPhotoKey = DefaultsKey<NSArray>("image")//Setting up the SwiftyUserDefaults Persisted Array
cardImages = [(UIImage(named: "MyImageName.jpg")!)] //This array contains the default value, and will fill up with more
Defaults[theKeyForStoringThisArray] = cardImages //This is the persisted array in which the array full of images should be stored. WHERE THE ERROR HAPPENS
var arrayToRetreiveWith = Defaults[theKeyForStoringThisArray] as! [UIImage] //To Retreive
Error
I get the following error:
Attempt to set a non-property-list object (
", {300, 300}"
) as an NSUserDefaults/CFPreferences value for key image
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object (
", {300, 300}"
) for key image'
Thanks!
The error message is clear actually. UIImage is not a propertylist so you need to change it to a row data first. I'll put example below but FYI saving big data like images using NSUserDefaults is really not recommended. I'd use NSFileManager and put it in the user documents directory. anyway
var newPhotoKey = DefaultsKey<NSArray>("image")
cardImages = [(UIImage(named: "MyImageName.jpg")!)]
var cardImagesRowdataArray: NSData = []
for image in cardImages {
let imageData = UIImageJPEGRepresentation(image, 1.0)
cardImagesRowdataArray.append(imageData)
}
Defaults[theKeyForStoringThisArray] = cardImagesRowdataArray
var arrayToRetreiveWith = Defaults[theKeyForStoringThisArray] as! [NSData]
// here you can use UIImage(data: data) to get it back
If you don't insist on using SwiftyUserDefaults, you can save it in the user documents directory, here is how to do it
func saveImage(image: UIImage){
if let imageData = UIImageJPEGRepresentation(image, 1.0) {
let manager = NSFileManager()
if let docUrl = manager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first{
let uniqueName = NSDate.timeIntervalSinceReferenceDate()
let url = docUrl.URLByAppendingPathComponent("\(uniqueName).jpg")
imageData.writeToURL(url, atomically: true)
}
}
}
The value of a user default must be a property list. A property list is
a string (String or NSString),
an NSData,
a date (NSDate),
a number (NSNumber),
a boolean (also NSNumber),
an array of property lists,
or a dictionary whose keys are strings and whose values are property lists.
A UIImage is none of those, so a UIImage is not a property list and cannot be part of a property list.
You need to convert your image to an NSData to store it in a user default. Since a UIImage contains some properties (like scale and imageOrientation) in addition to raw pixel data, the easiest way to convert a UIImage to an NSData with no loss is by creating an archive:
let cardImage: UIImage? = someImage()
let cardImageArchive: NSData = NSKeyedArchiver.archivedDataWithRootObject(cardImage!)
Now you can store cardImageArchive in a larger property list that you can store as a user default.
Later, when you need to recreate the image from the data, do this:
let cardImageArchive: NSData = dataFromUserDefaults()
let cardImage: UIImage = NSKeyedUnarchiver.unarchiveObjectWithData(cardImageArchive) as! UIImage
I want to store a dictionary as:
[String : AnyItem]
because the format will be
["codename" : "title", "image" : UIImage("imageName")]
but I am having a difficult type accomplishing this. The ultimate goal is to have an array full of these types of dictionaries.
If you want a dictionary value that can hold anything, you need Any (rather than AnyType.
But this will be a real pain – whenever you want to get the value out, you’ll have to covert the types back every time, and if you mess up doing that, you’ll get all sorts of errors.
Instead, consider using a tuple – a pair of the two types you want. You can name the elements to make them easier to access:
var images: [(codename: String, image: UIImage)] = []
if let url = NSURL(string: "http://www.gravatar.com/avatar/24d29e1ff91b68642bbef96b43b5e119?s=128&d=identicon&r=PG&f=1"),
let data = NSData(contentsOfURL: url),
let image = UIImage(data: data) {
images.append(codename: "AlexAvatar", image: image)
}
images[0].codename // name of the first image in the array
images[0].image // the first image in the array
Alternatively, if you are going to want to address the images by name, why not use the “codename” field as the dictionary key?
var images: [String:UIImage)] = []
if let url = NSURL(string: "http://www.gravatar.com/avatar/24d29e1ff91b68642bbef96b43b5e119?s=128&d=identicon&r=PG&f=1"),
let data = NSData(contentsOfURL: url),
let image = UIImage(data: data) {
images["AlexAvatar"] = image
}
for (codename, image) in images {
// iterate over the codename/image pairs
}
If your actual data requirements are more complicated, consider a struct instead of a tuple.