Array: Swift and IOS - ios

All,
Can you give me a source code of my below problem?
I have 100 user array and load that array value(username) in tableview.
Scenario:
When I click any tableviewcell, i store that value in new array( userArray) and also i have displayed right side discloser (checkmark).
But my problem is when i click again to deselect that user from tablecell my app has been crashed due to index out of bound.
I know problem is out of index, but how can i resolve that issue so if i click again on that tablecell it will remove user from userArray and i get new fresh userArray?
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let tagValue = 100 + indexPath.row
let cell: UITableViewCell = tableView.viewWithTag(tagValue) as UITableViewCell
if cell.accessoryType == UITableViewCellAccessoryType.None
{
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
userToSendPost.append(self.users2[indexPath.row])
} else {
cell.accessoryType = UITableViewCellAccessoryType.None
let tempString = userToSendPost[indexPath.row] //tagvalue
let objcArray = userToSendPost as NSArray
let indexOfObject = objcArray.indexOfObject(tempString)
userToSendPost.removeAtIndex(indexPath.row)
}
}
sorry for my bad english.

You are approaching this incorrectly.
On the tableview set the property tableView.allowsMultipleSelection = true.
Then when you want to get the selected users...
let selectedIndexPaths = tableView.indexPathsForSelectedRows()
Then you can use the row property from those index paths to find the users.
var selectedUsers: [User] = []
for indexPath in selectedIndexPaths {
let user = theUsers[indexPath.row]
selectedUsers.append(user)
}
The multiple selection and the check mark is handled automatically by the tableview.

To follow your approach, I think this could help you ;)
But the approach of #Fogmeister is better !
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let tagValue = 100 + indexPath.row
let cell: UITableViewCell = tableView.viewWithTag(tagValue) as UITableViewCell
if cell.accessoryType == UITableViewCellAccessoryType.None
{
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
userToSendPost.append(self.users2[indexPath.row])
} else {
let tempUserString = self.users2[indexPath.row] // get your user string
if contains(userToSendPost, tempString) { // check if exist on your array
userToSendPost.removeObject(tempString) // remove it
}
cell.accessoryType = UITableViewCellAccessoryType.None
}
}
// If you want to use Array, add this method
extension Array {
mutating func removeObject<U: Equatable>(object: U) {
var index: Int?
for (idx, objectToCompare) in enumerate(self) {
if let to = objectToCompare as? U {
if object == to {
index = idx
}
}
}
if(index != nil) {
self.removeAtIndex(index!)
}
}
}

Related

Multiple selection UITableView without dequeueReusableCellWithIdentifier

When selecting multiple cells in my tabeview the cells out of view are being selected too. I understand that this is because i am reusing the cell and its maintaining its selection as i scroll down. I have found a few people with similar issues but cant translate their solutions across to resolve my issue. I have tried not dequeing a cell and just use:
let cell = NewBillSplitterItemCell()
but get:
unexpectedly found nil while unwrapping an Optional value
on the line:
cell.currentSplitters.text = splitterList
in the following code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
fetchBillItems()
let cell: NewBillSplitterItemCell = tableView.dequeueReusableCellWithIdentifier("NewBillSplitterItemCell") as! NewBillSplitterItemCell
let item = allItems[indexPath.row]
let numberOfSplitters = item.billSplitters?.count
if numberOfSplitters == 0 {
cell.currentSplitters.text = "No one is paying for this item yet."
} else {
var splitterList = "Split this item with "
let itemSplitters = item.billSplitters?.allObjects as! [BillSplitter]
for i in 0...Int((numberOfSplitters)!-1) {
if numberOfSplitters == 1 {
splitterList += "\(itemSplitters[i].name!)"
} else {
splitterList += ", \(itemSplitters[i].name!)"
}
}
cell.currentSplitters.text = splitterList
}
cell.name.text = item.name
cell.price.text = "£\(item.price!)"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .Checkmark
{
cell.accessoryType = .None
selectedItems.removeAtIndex(selectedItems.indexOf(allItems[indexPath.row])!)
} else {
cell.accessoryType = .Checkmark
selectedItems.append(allItems[indexPath.row])
}
}
}
I dont quite understand what to do and any help would be great. Thanks
In addition to what #Mike said, inside of cellForRowAtIndexPath you need an additional check because cells get reused.
Something along the line
let isSelected = selectedItems[indexPath.row].selected
if isSelected{
cell.accessoryType = .Checkmark
} else {
cell.accessoryType = .None
}
Same thing inside of didSelectRowAtIndexPath you should update the data source instead of relying on the UI of your cell for that condition.
Assuming your cell is nil, you should use
let cell = tableView.dequeueReusableCellWithIdentifier("..." forIndexPath:indexPath) as! NewBillSplitterItemCell
instead of
let cell= tableView.dequeueReusableCellWithIdentifier("...") as! NewBillSplitterItemCell
This ensures that cell will never be nil.
Also, I would check if the correct identifier is being used in all of your .xib .storyboard files.

multiple rows gets selected while selecting a single row on tableView

am having this strange issue while am selecting any of the row from TableView another row is also get selected , say i have a tableView with multiple rows in my case its 11 and multiple selection with the accessory of tick mark is enabled (when i select a row a tick is marked on the selected row ) so when am selecting my first row , then row number 8 is also got selected (i can see the tick mark in the row number 8 but i selected only the row number 1 ) when i select another row number 2 my row number 9 is also get selected dont know why this is happening if anybody knows anything about this behaviour then please let me know it'll be so helpful for me , below is the code of didSelectRowAtIndexPath :
var selectedTextLabels = [NSIndexPath: String]()
var selectedTextLabelsName = [NSIndexPath: String]()
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let cell = tableView.cellForRowAtIndexPath(indexPath) as! UsersTableViewCell
if (cell.accessoryType == UITableViewCellAccessoryType.Checkmark){
cell.accessoryType = UITableViewCellAccessoryType.None;
selectedTextLabels[indexPath] = nil
selectedTextLabelsName[indexPath] = nil
}else{
cell.accessoryType = UITableViewCellAccessoryType.Checkmark;
if (cell.accessoryType == UITableViewCellAccessoryType.Checkmark){
if let Id = cell.channelIDLbl?.text {
selectedTextLabels[indexPath] = Id
}
if let channelName = cell.lblChannelname?.text{
selectedTextLabelsName[indexPath] = channelName
}
}
}
cellForRowAtIndexpath:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UsersTableViewCell
if isFavoritesTabActive == true {
let object : RChannels = self.favoritesArray.objectAtIndex(indexPath.row) as! RChannels
cell.lblChannelname.text = object.channelName
let favorite = object.isFavorite
if favorite == "true" {
cell.favIcon.image = UIImage(named: "Favourite")
return cell }
else {
let object : RChannels = self.noteObjects.objectAtIndex(indexPath.row) as! RChannels
cell.lblChannelname.text = object.channelName
let favorite = object.isFavorite
if favorite == "true" {
cell.favIcon.image = UIImage(named: "Favorite")
}else {
cell.favIcon.image = UIImage(named: "unFavorite") }
return cell
}
checking inside the channelIDLbl array for existence of the cell's id at cellforRowAtIndexPath did the job
if ((selectedTextLabels[indexPath]?.containsString("\(cell.channelIDLbl)")) != nil){
cell.accessoryType = .Checkmark
}else {
cell.accessoryType = .None
}
for more detail please check this same question

Parse and Swift. Set tableViewCell accessory type from Parse relation

So, I have got a tableView which shows courses. The user is able to set Checkmarks on these courses (cells) and save them in his PFUser object as a relation to the Courses class (where all courses are stored).
My question is, how do I checkmark the courses a user has already saved at some point before.
This is my attempt, but I don’t know how to continue. How do I get the cells with a specific Label? (Or is there a better way?)
let courseRel = PFUser.currentUser()?.relationForKey("usercourses")
let query = courseRel!.query()
let qObjects :Array = query!.findObjects()!
println(qObjects)
for var qObjectsCount = qObjects.count; qObjectsCount > 0; --qObjectsCount {
var qAnObject: AnyObject = qObjects[qObjectsCount - 1]
var courseName = qAnObject["coursename"]
println(courseName)
if let cell: AnyObject? = tableView.dequeueReusableCellWithIdentifier("courseCell"){
}
}
EDIT: that code is in my override viewDidLoad
EDIT2:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("courseCell") as! PFTableViewCell!
if cell == nil {
cell = PFTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "courseCell")
}
let courseRel = PFUser.currentUser()?.relationForKey("usercourses")
let query = courseRel!.query()
let qObjects :Array = query!.findObjects()!
// Extract values from the PFObject to display in the table cell
if let courseName = object?["coursename"] as? String {
cell?.textLabel?.text = courseName
if contains(qObjects, object) {
cell?.accessoryType = UITableViewCellAccessoryType.Checkmark
}
}
return cell
}
Error in line ‚if contains(qObjects, object) {'
Generic parameter 'S.Generator.Element’ cannot be bound to non-#objc protocol type 'AnyObject'
EDIT3:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("courseCell") as! PFTableViewCell!
if cell == nil {
cell = PFTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "courseCell")
}
let courseRel = PFUser.currentUser()?.relationForKey("usercourses")
let query = courseRel!.query()
let qObjects :Array = query!.findObjects()!
// Extract values from the PFObject to display in the table cell
if let courseName = object?["coursename"] as? String {
cell?.textLabel?.text = courseName
cell.tintColor = UIColor.blackColor()
}
if contains(qObjects, { $0 === object }) {
cell?.accessoryType = UITableViewCellAccessoryType.Checkmark
self.selectedRows.addIndex(indexPath.row)
}else{
cell?.accessoryType = UITableViewCellAccessoryType.None
}
return cell
}
EDIT4: (Working code)
In the class:
// Initializing qObject variable
var qObjects :Array<AnyObject> = []
In my objectsDidLoad:
// Get PFObjects for the checkmarks on courses the currentUser has already selected before
let courseRel = PFUser.currentUser()?.relationForKey("usercourses")
let query = courseRel!.query()
qObjects = query!.findObjects()!
In my tableView(cellForRowAtIndexPath):
// Extract values from the PFObject to display in the table cell
if contains(qObjects, { $0 === object }) {
cell.accessoryType = UITableViewCellAccessoryType.Checkmark
self.selectedRows.addIndex(indexPath.row)
} else {
cell.accessoryType = UITableViewCellAccessoryType.None
}
Don't try to search for a cell. Well, you can, but not like you're trying to - I'll come back to that.
Your current code is creating new cells, not finding existing cells, so that won't work.
What you should really be doing is storing the array of returned objects, qObjects, and then when you're configuring the cell for display checking if that array contains the object for the current cell. If it does, tick it, otherwise remove the tick.
Now, if the load of qObjects happens after the view is shown you have 2 options:
reload the table view
update just the visible items
Option 2. is obviously better, especially if the user might be scrolling the list. To do that you want to use the array returned by calling indexPathsForVisibleRows on the table view. Then, iterate that list, get the associated object and check if it's in qObjects, then get the cell on display with cellForRowAtIndexPath: and update it.

Multiple cell selection with search

I've created a UITableView which contains a list of teams which all have a accessoryView. This accessoryView indicates whether a cell is selected or not. Each cell is connected with a custom Object in my Team class.
When a cell is selected it then saves the particular team Object in a teamSelected array.
All this works fine. However there seem to be a issue when search and then filter the data and select a cell it seem to add the wrong object and change the accessoryView on a wrong object to?
How come it does to not add the correct object on cell selection when the searchBar is active?
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("teamCell", forIndexPath: indexPath) as! TeamCell
if (self.teamSearchController.active) {
cell.textLabel?.text = filteredTableData[indexPath.row].name
} else {
cell.textLabel?.font = UIFont(name: "HelveticaNeue-Light", size: 20)
cell.textLabel?.text = self.teamArray[indexPath.row].name as String
}
let team = self.teamArray[indexPath.row] as Team
var removed = false
for (index, value) in enumerate(self.teamSelected) {
if (value.id == team.id) {
cell.accessoryView = cell.accessoryCheck
removed = true
}
}
if (!removed) {
cell.accessoryView = cell.accessoryUncheck
}
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
if (self.teamSearchController.active) {
team = self.filteredTableData[indexPath.row] as! Team
removed = false
} else {
team = self.teamArray[indexPath.row] as Team
removed = false
}
for (index, value) in enumerate(self.teamSelected) {
if (value.id == team.id) {
self.teamSelected.removeAtIndex(index)
removed = true
}
}
if (!removed) {
self.teamSelected.append(team)
}
var userDefaults = NSUserDefaults.standardUserDefaults()
let encodedData = NSKeyedArchiver.archivedDataWithRootObject(self.teamSelected)
userDefaults.setObject(encodedData, forKey: "teams")
userDefaults.synchronize()
tableView.reloadData()
}
I have done this in objective c. I have also used uisearchview Controller. I have use this code. cellSelected is a nsmutable array. Use this code in didSelect method of table View.
if (tableView == self.searchDisplayController.searchResultsTableView)
{
if ([self.cellSelected containsObject:[self.searchArray objectAtIndex:indexPath.row][#"id"]])
{
[self.cellSelected removeObject:[self.searchArray objectAtIndex:indexPath.row][#"id"]];
}
else
{
[self.cellSelected addObject:[self.searchArray objectAtIndex:indexPath.row][#"id"]];
[self.selectedCategoryArray addObject:getSelectedCategory];
}
}
after this use this code in cellforrowatindexpath method
if (tableView == self.searchDisplayController.searchResultsTableView)
{
NSLog(#"self.searchArray..%#",self.searchArray);
titleName.text = [self.searchArray objectAtIndex:indexPath.row][#"name"];
if ([self.cellSelected containsObject:[self.searchArray objectAtIndex:indexPath.row][#"id"]])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
In the above code make else and use your actual array. hope this will help you. Thanks

CollectionView objects (Swift)

I want the highlight to change the size and appearance of an object inside the collection view.
How can I set object properties in a collection view cell, within the "didHighlight" method?
In "cellForItemAtIndexPath" you declare the reusable cells as the class
and just use "cell.MyOutlet.backgroundColor = UIColor.blueColor()"
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if collectionView == self.CollectionViewController {
let (FriendFirstName,FriendLastName) = friends[indexPath.row]
let cell: CustomCellA = collectionView.dequeueReusableCellWithReuseIdentifier("demoCell", forIndexPath: indexPath) as! CustomCellA
if indexPath.section == 0 {
cell.cellTitle.text = Name
cell.imgCell.image = UIImage(named: Pics[indexPath.row])
cell.imgCell.layer.masksToBounds = true
cell.self.imgCell.layer.cornerRadius = 20
return cell
} else {
let cell2: AddCell = collectionView.dequeueReusableCellWithReuseIdentifier("demoCell2", forIndexPath: indexPath) as! AddCell
return cell2
}
} else if collectionView == self.EmojiCollectionViewController {
let cellB: CustomCellB = collectionView.dequeueReusableCellWithReuseIdentifier("demoCellB", forIndexPath: indexPath) as! CustomCellB
cellB.MyLabel.text = arrayOne[indexPath.row]
return cellB
} else {
let cellC: CustomCellC = collectionView.dequeueReusableCellWithReuseIdentifier("demoCellC", forIndexPath: indexPath) as! CustomCellC
// ...Set up cell
let height = self.CollectionViewController2.frame.height
cellC.frame = CGRectMake(cellB.frame.origin.x, 0, cellB.frame.size.width, height)
cellC.updateConstraintsIfNeeded()
cellC.layoutIfNeeded()
cellC.imgVw.image = UIImage(named: pictures[indexPath.row] as! String)
return cellC
}
}
func collectionView(collectionView: UICollectionView, didHighlightItemAtIndexPath indexPath: NSIndexPath) {
if collectionView == self.CollectionViewController {
if indexPath.section == 0 {
let cell: CustomCellA = CustomCellB()
cell.MyLabel.backgroundColor = UIColor.blueColor() //crashes due to nil value)
}
} else {
}
}
I tried using a similar definition in didHighlight and it keeps crashing.
Let didHighlightItemAtIndexPath only change the data, not the view. So, make friends[indexPath.row] an object or add another parameter to tuple. And in didHighlightItemAtIndexPath do something like the following:
if collectionView == self.CollectionViewController {
if indexPath.section == 0 {
let (fname, lname, color) = friends[indexPath.row];
friends[indexPath.row] = (fname, lname, UIColor.blueColor())
}
}
And in cellForItemAtIndexPath:
if collectionView == self.CollectionViewController {
let (FriendFirstName, FriendLastName, color) = friends[indexPath.row]
if indexPath.section != 0 {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("demoCell2", forIndexPath: indexPath) as! AddCell;
return cell;
} else if color == nil {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("demoCell", forIndexPath: indexPath) as! CustomCellA;
cell.cellTitle.text = Name
cell.imgCell.image = UIImage(named: Pics[indexPath.row])
cell.imgCell.layer.masksToBounds = true
cell.self.imgCell.layer.cornerRadius = 20
return cell
} else {
cell = collectionView.dequeueReusableCellWithReuseIdentifier("demoCellB", forIndexPath: indexPath) as! CustomCellB;
// your code for CustomCellB
return cell;
}
}
EDIT: Updated, so instead of objects it uses tuples. Also added the functionality that you need. Basically, you need to create two prototype cells in the interface builder with different Reuse Identifiers and Classes. And then dequeue the correct identifier in the index path. Also, I refactored some of your code and if I were you I would create a different function for each collectionView and do something like:
if collectionView == self.CollectionViewController {
return self.dequeueCollectionCell(indexPath);
} else if collectionView == self.EmojiCollectionViewController {
return self.dequeuEmojiCell(indexPath);
} else {
return self.dequeueSomeOtherCell(indexPath);
}
Also, the code that you provided... I hope it is not an actual production code and you changed the values for this forum. Otherwise, in couple of days even, you are going to get lost in what is happening here. Too many inconsistent variable names and identifiers.
One more also. Use naming conventions in your class names. Read this forum post for more information. Apple uses camelCase everywhere. In majority of instances, the first letter is capitalized for class names, not object names.
first you have to define the collectionView Cell then do what ever you want on that cell. to define your sell add the below lines into didHighlightItemAtIndexPath
if let cellToUpdate = self.dataCollection.cellForItemAtIndexPath(indexPath) {
//your code here.
}

Resources