How to initialize a two dimensional array of UIButtons in Swift? - ios

I'm trying to convert a one dimensional array of UIButtons into a two dimensional array of them. In order to do this, I need to instantiate an empty two-dimensional array of UIButtons with nil values that I could then point to the respective UIButton in the one-dimensional UIButton array. I'm getting confused by Optionals since I'm new to Swift. Here's my one-dimensional array:
#IBOutlet var noteUIButtonArray: [UIButton]?
And I would like to convert this 128-size one-D array into a two-D [8][16] array of UIButtons with will values. How would I do this? Thanks in advance.

If an array value can be nil, then the type it is holding must be an optional type. You need a two-dimensional array of UIButton? which is just an array of array of UIButton? or [[UIButton?]].
Arrays have a handy initializer that takes the count of the items and the value you want to initialize with and creates the array. In your case, with a two-dimensional array, you will need to use this initializer twice: once for each row of the array and once for each column.
The inner initializer will create an array of 16 nil values:
let row:[UIButton?] = Array(count: 16, repeatedValue: nil)
The outer initializer will create an array of 8 rows:
var buttons2D:[[UIButton?]] = Array(count: 8, repeatedValue: row)
You typically nest both of these together to initialize a two-dimensional array like so:
var buttons2D:[[UIButton?]] = Array(count: 8, repeatedValue: Array(count: 16, repeatedValue: nil))
Note that the original array is an optional array of UIButton or [UIButton]?. This means that the array might not exist at all (it can be nil), or if it does exist it holds items of type UIButton. This array cannot hold nil values. When accessing a value in this array, you have to unwrap the array value. A safe way to do that is to use optional chaining.
For example, to access the first button in noteUIButtonArray, you'd write:
let button = noteUIButtonArray?[0]
The ? unwraps the optional array and allows you to access item 0. If noteUIButtonArray is nil, then the optional chain will be nil, so button will receive the value nil. Since button can receive nil, its type is UIButton?. So, even though the array can't hold optional values, you will receive an optional value from the optional chain which then will need to be unwrapped to use it.
Then you could loop through your array of 128 buttons an assign them to your 2D array:
if noteUIButtonArray?.count >= 128 {
var i = 0
for row in 0..<8 {
for col in 0..<16 {
buttons2D[row][col] = noteUIButtonArray?[i++]
}
}
}
else {
print("Warning: there aren't 128 buttons")
}
You might want to assign your buttons in a different order. This is just an example of one way to do it.

Related

Appending items to an array in dictionary not working

I have this static dictionary created as so:
static var pictures = Dictionary<Int, Array<UIImage>>()
I want to populate it with images. At the moment when I am creating it I don't know how many key/value pairs I need to create. I have to fetch from the internet the data, but after that I am doing this to populate, but still my dictionary is empty:
for i in 0...Fetching.numberOfAliveListings - 1 {
for _ in 0...AdsCollectionView.listings[i].photos.count - 1 {
AdsCollectionView.pictures[i]?.append(UIImage(named: "noimage")!)
}
}
pictures is initially empty. So any attempt to access a value for a given key will result in a nil value. Since the value (the array) is nil, the optional chaining skips the call to append.
One solution is to provide a default array when looking up the value for a given Int.
AdsCollectionView.pictures[i, default: []].append(UIImage(named: "noimage")!)
You may also wish to consider alternate syntax when declaring pictures:
static var pictures = [Int: [UIImage]]()

Multidimensional Array Looping in cellForRowAtIndexPath Swift

I have a multidimensional array that I want to display the values of onto one UILabel in each respective cell.
My arrays look like this:
var arrayExample = [["beverages", "food", "suppliers"]["other stuff, "medicine"]]
I'm looping through these values in the cellForRowAtIndexPath in order for it to display on different cells (on a UILabel) the appropriate values:
if let onTheLabel: AnyObject = arrayOfContactsFound as? AnyObject {
for var i = 0; i < objects!.count; i++ {
cell?.contactsUserHas.text = "\(onTheLabel[indexPath.row][i])" as! String
print("arrayOfContactsFound Printing! \(onTheLabel)")
}
}
When printing to the console I get:
arrayOfContactsFound Printing! (
(
beverages,
"supply chain",
pharmacuticals
)
)
But on my label I get "beverages". That's it. How can I get the other 2 values (or X amount if there are more or less than 3 values)?
My for in loop is obviously not doing the trick. Assuming I can optimize/fix that to display all the values?
Thanks in advance.
In your loop you're setting the text of your label multiple times. Each time you set it it doesn't accumulate, it completely replaces the current text with the new text. You'll want something like this:
// Remove the cast and the loop in your code example, and replace with this
let items = arrayOfContactsFound[indexPath.row]
let itemsString = items.joinWithSeparator(" ")
cell?.contactsUserHas.text = itemsString
Another thing to note is your cast doesn't quite make a lot of sense.
var arrayExample = [["beverages", "food", "suppliers"]["other stuff, "medicine"]]
So arrayExample is of type [[String]]. I'm assuming each cell in your table view represents one of the arrays of strings in your array. So each cell represents one [String]. So your items should be arrayExample[indexPath.row]. The cast to AnyObject doesn't make too much sense. If anything you'd be casting it to [[AnyObject]], but there's no reason to because the compiler should already know it's [[String]].

Can you populate a UITableView without using the cellForRowAtIndexPath method?

The reason I ask is because I have a Dictionary of objects that I would like to populate my UITableView with. Since the Dictionary has objects for both the key and value, I'm unsure of how to populate the UITableView with it. The only way I know of to populate a UITableView is to use cellForRowAtIndexPath which requires an index (indexPath.row) and since my Dictionary is a collection of objects, I'm unable to reference each object by index.
Does anyone know if you can populate a UITableView without using cellForRowAtIndexPath method? Or, is there a way to populate the UITableView with the Dictionary of object by using cellForRowAtIndexPath?
As per my comment:
Dictionaries do not have a fixed order, so this would possibly yield different results on each run of the app.
So convert the dictionary to an array of the key/value tuples.
To convert a dictionary to an array (substitute your correct types as appropriate):
let dictionary = ["a" : 1, "b" : 2, "c" : 3]
var array = [(String, Int)]()
for tuple in dictionary {
array.append(tuple)
}
println(array)
Outputs:
[(b, 2), (a, 1), (c, 3)]

"Cannot assign to" error iterating through array of struct

I have an array of structs:
struct CalendarDate {
var date: NSDate?
var selected = false
}
private var collectionData = [CalendarDate]()
Which I simply populate with a date like this:
for _ in 1...7 {
collectionData.append(CalendarDate(date: NSDate(), selected: false))
}
So when you tap on a collectionView, I simply want to loop through the data and mark them all as False.
for c in collectionData {
c.selected = false ///ERROR: Cannot assign to 'selected' in 'c'
}
Why do I get this error?
If I do this, it works fine but I want to know what I did wrong above:
for i in 0..<collectionData.count {
collectionData[i].selected = false
}
As I understand it, the iterator
for c in collectionData
returns copies of the items in collectionData - (structs are value types, not reference types, see http://www.objc.io/issue-16/swift-classes-vs-structs.html), whereas the iteration
for i in 0..<collectionData.count
accesses the actual values. If I am right in that, it is pointless to assign to the c returned from the iterator... it does not "point" at the original value, whereas the
collectionData[i].selected = false
in the iteration is the original value.
Some of the other commentators suggested
for (var c) in collectionData
but although this allows you to assign to c, it is still a copy, not a pointer to the original, and though you can modify c, collectionData remains untouched.
The answer is either A) use the iteration as you originally noted or B) change the data type to a class, rather than a struct.
because each 'c' is by default let, and this is a new instance of CalendarDate and the value of array at index copied to this for each step of for, and 'c' isn't pointer to the index of the array and it is just a copy of index, so if you set a new value to this, the new value does not apply in array.
but 'i' is used as index of array and can directly manipulate the values of array.
If you are using structs they are copies in the array. So even changing them only changes the copy, not an actual object in the array.
You have to make them a variable in the loop to be editable copy, and reassign them into the array right back.
If they are classes and not structs, than you don't have to reassign part, just do the var thing.
for (index, var c) in collectionData.enumerated() {
c.selected = false
collectionData[index] = c
}

How does one create a mutable copy of an immutable array in swift?

Now that Swift's Array's are truly immutable thanks to full value semantics, how can I create an mutable copy of an immutable array? Similar to Obj-C mutableCopy(). I can of course downcast the array to an NSArray and use mutableCopy() but don't want to use NSArray because it does not strictly typed.
I have a toolbar which has items from the storyboard. I want to remove an item from the toolbar and use toolbar.setItems. I wanted to do it without casting as a NSArray, because none of these functions take NSArrays, they take [AnyObject].
Obviously now when I call removeAtIndex() it does not work, which is correct. I just need a mutableCopy
Simply assigning to var does not work for me and give 'Immutable value of type [AnyObject]'
var toolbarItems = self.toolbar.items
toolbarItems.removeAtIndex(2) //Immutable value of type [AnyObject]
I am using Beta 3
The problem is that self.toolbar.items is an implicitly unwrapped optional (of type [AnyObject]!) and they are always immutable. When you assign to the variable toolbarItems without explicitly stating its type, it too becomes an implicitly unwrapped optional, and thus is immutable as well.
To fix this do either:
var toolbarItems:[AnyObject] = self.toolbar.items
toolbarItems.removeAtIndex(2)
Or:
var toolbarItems = self.toolbar.items as [AnyObject]
toolbarItems.removeAtIndex(2)
Update
As of Xcode 6 Beta 5, you can update collections that are stored in optional variables, so the original code now works:
var toolbarItems = self.toolbar.items
toolbarItems.removeAtIndex(2)
Arrays are value types (struct), so they are passed around by value and not by reference.
That said, if you create a variable of array type and assign it the immutable array, a copy of the immutable array is actually created and assigned to it - and of course that copy has no relationship with the original immutable array (besides having the same values at the time it is created).
let immutable = [1, 2, 3]
//immutable[0] = 1 // Fails, ok
var mutable = immutable
mutable[0] = 5
In your case, you are accessing an immutable array which is an NSArray of AnyObjects (see documentation). You can use it as an Array in swift, make a copy of it and modify as follows:
let immutable : NSArray = [ 1, 2, 3 ]
//immutable[0] = 1 // Fails, ok
var mutable : [AnyObject] = immutable
mutable.removeAtIndex(1) // mutable now is [1, 3]
mutable[0] = 7 // mutable now is [7, 3]
After you're done with your changes, you can assign to the items property
It is as simple as declaring a var with your array.
var items = toolbar.items
Now you can change items and then reassign to the toolbar.
toolbar.items = items
Note that you can cannot (as of Beta 3) alter the elements of an "immutable" array declared with let as well. Just the length of the array is was fixed, which is why you cannot remove items.
However, according to Apple's documentation of UIToolbar, the items array is already mutable.
SWIFT
var items: [AnyObject]!
Tested + works:
var mutable : [UIBarButtonItem] = []
for button in toolbar.items {
mutable += button as UIBarButtonItem
}
mutable.removeAtIndex(2)
toolbar.setItems(mutable, animated: true)
TO REMOVE AN OBJECT FROM PARTICULAR INDEX OF AN ARRAY.
let fullArray : NSArray = Userdefaults().value(forKey: "YOUR_ARRAY_STRING") as! NSArray
var mutableArray : [AnyObject] = fullArray as [AnyObject]
mutableArray.remove(at: INDEX_TO_REMOVE) //Eg: mutableArray.remove(at: 0)
mutableArray.append(ARRAY_TO_APPEND)
In Beta3 constant arrays are completely immutable while variable arrays are entirely mutable. So just change let array:to var array: and then verify your code

Resources