Multiple cell selection with search - ios

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

Related

Blank cells appear and dissapear randomly in UITableView

I'm trying to simulate a Chat Messages, and after insert some new cells, some of the oldest dissapear. And when I scroll appears again and disappear. I've tried all solutions that I found from here on SO but nothing works and I have not much idea frorm where error can come.
I'm not sure what code should I post to you tried to help, I will post my TableView code so maybe I'm doing something wrong or if you need anything else, just let me know.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.messagesCell.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return self.messagesCell[indexPath.row]
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let message = self.messages[indexPath.row]
if message.messageType == 2 {
output.setImageUrl(message.text)
router.navigateToGroupChatMessagesScene()
}
else {
self.view.endEditing(true)
}
}
This code is how I generate the cells everytime a new message is inserted:
func getMessageCell(withDisplayedMessage displayedMessage: GroupChatMessages.GetChatMessages.displayedChatMessage) -> GroupChatCell {
switch displayedMessage.messageType {
case 0:
if displayedMessage.sender == self.currentUser.userID {
let cell = self.messagesTableView.dequeueReusableCellWithIdentifier("senderCell") as! GroupChatCell
dispatch_async(dispatch_get_main_queue()) {
cell.configureCellText(withText: displayedMessage.text, andUtcSendTime: displayedMessage.utcSendTime)
cell.selectionStyle = UITableViewCellSelectionStyle.None
}
return cell
}
let cell = self.messagesTableView.dequeueReusableCellWithIdentifier("receiverCell") as! GroupChatCell
cell.configureCellAttributted(withText: displayedMessage.text, andSenderName: displayedMessage.senderName, andUtcSendTime: displayedMessage.utcSendTime)
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
case 1:
let cell = self.messagesTableView.dequeueReusableCellWithIdentifier("announcementCell") as! GroupChatCell
dispatch_async(dispatch_get_main_queue()) {
cell.configureInformationCell(withText: displayedMessage.text)
cell.selectionStyle = UITableViewCellSelectionStyle.None
}
return cell
case 2:
if displayedMessage.sender == self.currentUser.userID {
let cell = self.messagesTableView.dequeueReusableCellWithIdentifier("senderImageCell") as! GroupChatCell
dispatch_async(dispatch_get_main_queue()) {
cell.configureSenderImageCell(withImageUrl: displayedMessage.text, andUtcSendTime: displayedMessage.utcSendTime)
cell.selectionStyle = UITableViewCellSelectionStyle.None
}
return cell
}
let cell = self.messagesTableView.dequeueReusableCellWithIdentifier("receiverImageCell") as! GroupChatCell
dispatch_async(dispatch_get_main_queue()) {
cell.configureImageCell(withImageUrl: displayedMessage.text, andSenderName: displayedMessage.senderName, andUtcSendTime: displayedMessage.utcSendTime)
cell.selectionStyle = UITableViewCellSelectionStyle.None
}
return cell
case 10: //SpecialCaseForSendingImages
let cell = self.messagesTableView.dequeueReusableCellWithIdentifier("senderImageCell") as! GroupChatCell
dispatch_async(dispatch_get_main_queue()) {
cell.configureSenderImageCell(withImageUrl: displayedMessage.text, andUtcSendTime: displayedMessage.utcSendTime)
cell.selectionStyle = UITableViewCellSelectionStyle.None
}
return cell
default:
return GroupChatCell()
}
Hope you can help, and any further information I will provide you as fast I can! Thank you so much.
EDIT:
Where I receive a new message I add a new row with message information in this function:
func displayMessages(viewModel: GroupChatMessages.GetChatMessages.ViewModel) {
let displayedMessage = viewModel.displayedMessages
print ("i'm here!")
if let messages = displayedMessage {
self.messages = messages
self.messagesCell = []
for index in 0..<messages.count {
let cell = self.getMessageCell(withDisplayedMessage: messages[index])
self.messagesCell.append(cell)
let indexPath = NSIndexPath(forRow: index, inSection: 0)
self.messagesTableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
print ("i'm here2!")
firstTime = false
// self.scrollToLastMessage(false)
self.setVisible(hiddenTableView: false, hiddenChatLoader: true)
self.messagesLoaded = true
}
}
Get rid of the dispatch_async you should already be on the main
thread.
Keep an array of Model objects NOT an an array of cells (in
your case it looks like it should be an array of
displayedMessage).
Also remember that these cells can be reused,
so any property that you set must always be updated. In other words
every if must have an else when configuring a cell.
Hope that helps.

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.

Disabling all UITableView cells except the last one - Swift

Currently working with UITableView in swift.
Since my table cell values are appending by clicking the button i want to figure out a way to disable interaction for all the previous cells, except the last one.
I've played with array-based approach a little, but it didn't work out the way i wanted.
var disabledRows = [0,1,2]
else
{
cell.backgroundColor = UIColor.orangeColor()
}
if (contains(disabledRows, indexPath.row)) {
cell.userInteractionEnabled = false
}
else {
cell.userInteractionEnabled = true
}
The issue with this approach is that i have to count all cells and then block the last one.
Maybe there are some easier approaches?
Any help much appreciated.
Update - here is my complete "cellForRowAtIndexPath" function
func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell
{
let cell: CustomCellForTableViewTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! CustomCellForTableViewTableViewCell
if indexPath.row % 2 == 0
{
cell.backgroundColor = UIColor.purpleColor()
}
else
{
cell.backgroundColor = UIColor.orangeColor()
}
if (contains(disabledRows, indexPath.row)) {
cell.userInteractionEnabled = false
}
else {
cell.userInteractionEnabled = true
}
let quest = arrayOfQuestions[indexPath.row]
cell.setCell(type.Grocery, optionno1:type.option1, optionno2:type.option2)
cell.mainText.text = type.Grocery
cell.optionOne.backgroundColor = UIColor.redColor()
return cell
}
In cellForRowAtIndexPath: method.
if (indexPath.row == yourDataSourceArray.count - 1) {
// enable cell
} else {
// disable cell
}

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.
}

Array: Swift and 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!)
}
}
}

Resources