I have a UITableView with 3 prototype cells and 3 custom cell classes:
FriendCell
FriendRequestCell and AddFriendCell.
Initialized, the table displays Friends.
If there are any FriendRequests, it displays them in the section above Friends.
If there are no FriendRequests, it only displays Friends.
However, I also have a UISearchBar that searches for users and when it has results, should return AddFriendCells and reload the table.
Instead, I get this:
Code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if searching == true {
if let cell = tableView.dequeueReusableCellWithIdentifier("AddFriendCell", forIndexPath: indexPath) as? AddFriendCell {
let resultCell = userResults[indexPath.row]
cell.configureCell(resultCell)
}
} else {
if friendRequests.isEmpty || (indexPath.section == 1) {
if let cell = tableView.dequeueReusableCellWithIdentifier("FriendCell", forIndexPath: indexPath) as? FriendCell {
let friendCell = friends[indexPath.row]
cell.configureCell(friendCell)
}
} else {
if (indexPath.section == 0) {
if let cell = tableView.dequeueReusableCellWithIdentifier("FriendRequestCell", forIndexPath: indexPath) as? FriendRequestCell {
let friendRequestCell = friendRequests[indexPath.row]
cell.configureCell(friendRequestCell)
}
}
}
}
return FriendCell()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if searching == true {
return 1
} else {
return friendsDataSource.count
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching == true {
return userResults.count
} else {
return friendsDataSource[section].count
}
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if searching == true {
return nil
} else {
if friendsDataSource.count > 1 {
if section == 0 {
return "Friend Requests"
} else if section == 1 {
return "Friends"
}
} else {
return "Friends"
}
return "Friends"
}
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if searching == true {
return 0
} else {
return 25
}
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
searching = true
searchBar.resignFirstResponder()
userResults = [UserProfile]()
activitySpinner.startAnimating()
if let searchText = searchBar.text {
let userProfileQuery = PFQuery(className: "UserProfile")
userProfileQuery.whereKey("username", containsString: searchText)
userProfileQuery.findObjectsInBackgroundWithBlock({ resultArray, error in
if error != nil {
print("there's been an error searching for users: \(error)")
} else if let resultArray = resultArray {
print("number of results: \(resultArray.count)")
self.parseResults = resultArray
for userProfile in resultArray {
let username = userProfile["username"] as! String
let profilePicUrl = userProfile["profilePicUrl"] as! String
let parseObjectId = userProfile.objectId!
let newProfile = UserProfile(username: username, profilePicUrl: profilePicUrl, parseObjectId: parseObjectId)
self.userResults.append(newProfile)
}
self.tableView.reloadData()
self.activitySpinner.stopAnimating()
}
})
}
}
Any ideas on the root of the problem?
OK, here is the code for multiple types of cell:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if searching == true {
let cell = tableView.dequeueReusableCellWithIdentifier("AddFriendCell", forIndexPath: indexPath) as! AddFriendCell
// ...configure your cell...
return cell
}
else{
if friendRequests.isEmpty || (indexPath.section == 1) {
let cell = tableView.dequeueReusableCellWithIdentifier("FriendCell", forIndexPath: indexPath) as! FriendCell
// ...configure your cell...
return cell
}
else{
if (indexPath.section == 0) {
let cell = tableView.dequeueReusableCellWithIdentifier("FriendRequestCell", forIndexPath: indexPath) as! FriendRequestCell
// ...configure your cell...
return cell
}
else {
// Decide what to do if section is NOT 0. If this CAN happen
// and you don't have a cell type to return, it is a design flaw.
// don't add a adummy "return FriendCell()" as a fallback for a
// case that should never happen, to make the compiler happy.
// This type of error should be caught during development.
}
}
}
}
(See the comment paragraph on how to deal with the unsuported execution path)
Alternatively, you could declare cell as a var of type UITableViewCell outside of all if/else blocks, assign it to the appropriatey dequeued cell inside, (i.e., remove the let keyword if modifying the code above), and return it at the very end.
But you still need to make sure it is initialized before returning.
if the method findObjectsInBackgroundWithBlock is asynchronous, I think you can use self.tableView.reloadData() instead of dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
add self.tableView.rowHeight = 50
and you don't return your addfriendcell. you just return FriendsCell() at the last line. add the return cell.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if searching == true {
if let cell = tableView.dequeueReusableCellWithIdentifier("AddFriendCell", forIndexPath: indexPath) as? AddFriendCell {
let resultCell = userResults[indexPath.row]
cell.configureCell(resultCell)
return cell! //add the return
}
} else {
if friendRequests.isEmpty || (indexPath.section == 1) {
if let cell = tableView.dequeueReusableCellWithIdentifier("FriendCell", forIndexPath: indexPath) as? FriendCell {
let friendCell = friends[indexPath.row]
cell.configureCell(friendCell)
return cell! //add the return
}
} else {
if (indexPath.section == 0) {
if let cell = tableView.dequeueReusableCellWithIdentifier("FriendRequestCell", forIndexPath: indexPath) as? FriendRequestCell {
let friendRequestCell = friendRequests[indexPath.row]
cell.configureCell(friendRequestCell)
return cell! //add the return
}
}
}
}
return FriendCell()
}
Related
Before duplicating this question, please be known that I've spent days on this issue, working hours, and looking for all same sort of questions on SO, but there is something I am missing or doing wrong.
I have a tableView in which the data is being populated via API response. Below is the model I have.
struct Model : Codable {
let bugClassification : [Bug]?
}
struct Bug : Codable {
let selectable : String? //Telling wether cell is single/Multi selected
var options : [Options]?
}
struct Options : Codable, Equatable {
let title : String?
let id: Int
var isCellSelected: Bool = false
}
Scenario
I want to create multiple sections, each having different cell depending upon the type of selectable, either single or multi. I have achieved that, but the problem I am getting is that whenever I scroll, random cells are also selected. Now, I know this behaviour is because of tableView reusing the cells. But I am confused as how to handle all this. Also, I want to put the validation on the sections, that is, every section should have atleast one cell selected. Kindly guide me in the right direction, and any small help would be appreciated. Below is my code.
CellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if bugClassification[indexPath.section].selectable?.lowercased() == "multi-select" {
//Multi-Selection
let cell = tableView.dequeueReusableCell(withIdentifier: multiSelectionCellID) as! MultiSelectionCell
let item = bugClassification[indexPath.section].options![indexPath.row]
cell.label.text = item.title
if item.isCellSelected {
cell.checkMarkImageView.alpha = 1
cell.checkMarkView.layer.borderColor = UIColor.white.cgColor
cell.checkMarkView.backgroundColor = .emerald
} else if item.isCellSelected {
cell.checkMarkImageView.alpha = 0
cell.checkMarkView.layer.borderColor = UIColor.veryLightBlue.cgColor
cell.checkMarkView.backgroundColor = .white
}
return cell
} else {
//Single-Selection
let cell = tableView.dequeueReusableCell(withIdentifier: singleSelectionCellID) as! SingleSelectionCell
let item = bugClassification[indexPath.section].options![indexPath.row]
cell.label.text = item.title
if item.isCellSelected {
cell.checkMarkImageView.alpha = 1
cell.checkMarkView.layer.borderColor = UIColor.emerald.cgColor
} else {
cell.checkMarkImageView.alpha = 0
cell.checkMarkView.layer.borderColor = UIColor.veryLightBlue.cgColor
}
return cell
}
}
DidSelectRow Method
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if bugClassification[indexPath.section].selectable?.lowercased() == "multi-select" {
var item = bugClassification[indexPath.section].options![indexPath.row]
item.isCellSelected = !item.isCellSelected
bugClassification[indexPath.section].options![indexPath.row] = item
self.tableView.reloadRows(at: [indexPath], with: .automatic)
} else {
let items = bugClassification[indexPath.section].options
if let selectedItemIndex = items!.indices.first(where: { items![$0].isCellSelected }) {
bugClassification[indexPath.section].options![selectedItemIndex].isCellSelected = false
if selectedItemIndex != indexPath.row {
bugClassification[indexPath.section].options![indexPath.row].isCellSelected = true
}
} else {
bugClassification[indexPath.section].options![indexPath.row].isCellSelected = true
}
self.tableView.reloadSections([indexPath.section], with: .automatic)
}
}
In cellForRowAt
if item.isCellSelected == true{
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
and update the model by every selection
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let item = bugClassification[indexPath.section].options![indexPath.row]
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
if indexPath.section == 0{
item.isCellSelected.isSelected = false
}else{
item.isCellSelected.isSelected = false
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = bugClassification[indexPath.section].options![indexPath.row]
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
if indexPath.section == 0{
item.isCellSelected.isSelected = true
}else{
item.isCellSelected.isSelected = true
}
}
}
I'm trying to delete a row from a section in my table with multiple sections using commit editingStyle. However, it's deleting the proper indexPath.row from the section above.
How can I get it to delete from the proper section?
I followed a couple of examples on how to section a single array and index it for the tableView. I am unable to properly delete from the initial Array of custom class objects. I am also unable to find a way to transfer the IndexPath Section and Row to a second view controller to display the selected Code. It just transfers the indexPath.row but I can't get it to send the entire indexPath including the section.
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if tableView == firstTableView {
if editingStyle == .delete {
if inSearchMode == false {
codeArray.remove(at: [indexPath.section][indexPath.row])
userDefaults.setValue(NSKeyedArchiver.archivedData(withRootObject: codeArray), forKey: "codeArrayKey")
userDefaults.synchronize()
tableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let subArrayArray = codeArray.compactMap { $0.subArray as? String }
var dic = [String:[Code]]()
subArrayArray.forEach {
let subArrayKey = $0
let filterArray = codeArray.filter { $0.subArray as? String == subArrayKey }
dic[$0] = filterArray
}
let sectionTitle = sectionTitles[section]
let sectionCodes:[Code] = dic[sectionTitle]!
if tableView == firstTableView {
if inSearchMode == true {
return filteredCodeArray.count
} else {
return sectionCodes.count
}
} else if tableView == autoTableview {
return locationFilteredCodeArray.count
} else {
return 1
}
}
func numberOfSections(in tableView: UITableView) -> Int {
if tableView == firstTableView {
if inSearchMode == false {
indexCodes(enterArray: codeArray)
return sectionTitles.count
} else if inSearchMode == true {
return 1
}
}
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == firstTableView {
if let cell = tableView.dequeueReusableCell(withIdentifier: "newCodesProtoCell") as? NewCodesViewCell {
let code: Code!
if inSearchMode == true {
code = filteredCodeArray[indexPath.row]
cell.configureCell(code: code)
} else {
let subArrayArray = codeArray.compactMap { $0.subArray }
var dic = [String:[Code]]()
subArrayArray.forEach {
let subArrayKey = $0
let filterArray = codeArray.filter { $0.subArray == subArrayKey }
dic[$0] = filterArray
}
let sectionTitle = sectionTitles[indexPath.section]
let sectionCodes:[Code] = dic[sectionTitle]!
code = sectionCodes[indexPath.row]
cell.configureCell(code: code)
}
return cell
}
}
if let cell = tableView.dequeueReusableCell(withIdentifier: "secondNewCodesProtoCell") as? SecondNewCodesProtoCell {
let code: Code!
if locationFilteredCodeArray.count != 0 {
locationAuthStatus()
code = locationFilteredCodeArray[indexPath.row]
cell.configureSecondCell(code: code)
}
return cell
}
return UITableViewCell()
}
This is how I'm getting the array of index names (headers) as this may be causing some of the issues.
This is written to index the [Code] by the second letter in the .location.
func indexCodes(enterArray: [Code]) {
var codeValues = [String]()
for code in enterArray {
var initCodeKey = String(code.location.prefix(2))
initCodeKey.remove(at: initCodeKey.startIndex)
let codeKey = initCodeKey.capitalized
codeValues.append(codeKey)
}
var encountered = Set<String>()
var result: [String] = []
for value in codeValues {
if encountered.contains(value) {
} else {
encountered.insert(value)
result.append(value)
}
}
sectionTitles = result.sorted(by: <)
}
You need to do 2 steps.
1. From your codeArray, get the dictionary from indexPath.section and then your Code object from [Code] saved in that dictionary. Remove code at indexPath.row and then reserve it to dictionary and replace dictionary object in CodeArray
2. self.tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .fade)
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if tableView == firstTableView {
if editingStyle == .delete {
if inSearchMode == false {
codeArray.remove(at: [indexPath.section][indexPath.row])
var dict = codeArray[indexPath.section]
let key = dict.allKeys.first
var codes = dict[key]
//remove code from [Code]
codes.remove(at: indexPath.row)
dict[key] = codes
codeArray[indexPath.section] = dict
userDefaults.setValue(NSKeyedArchiver.archivedData(withRootObject: codeArray), forKey: "codeArrayKey")
userDefaults.synchronize()
self.tableView.reloadRows(at: [IndexPath(row: indexPath.row, section: indexPath.section)], with: .fade)
}
}
}
let me know if you find any issue implementing it.
In this i am having three sections in a table view in which first section will have addresses and radio buttons if i click on radio button it will active and the particular address will be posting depending on the address selection the third section needs to call the api and load the data in the second table view which is present in third section here the problem is during loading for first time when app launched in simulator it is not loading the third section cell data can any one help me how to reduce the error ?
here is the code for table view class
func numberOfSections(in tableView: UITableView) -> Int
{
if ((addressSelected == true || checkIsPaymentRadioSelect == true) && selected == false) {
return 3
}else {
return 2
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
{
if ((addressSelected == true || checkIsPaymentRadioSelect == true) && selected == false) {
if (section == 0) {
return "SHIPPING ADDRESS"
}
else if (section == 2) {
return "SHIPPING METHOD"
}
else {
return ""
}
}
else {
if (section == 0) {
return "SHIPPING ADDRESS"
}
else{
return ""
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if (section == 0)
{
return shippingArray.count
}
else
{
return 1
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat{
if ((addressSelected == true || checkIsPaymentRadioSelect == true) && selected == false){
if (indexPath.section == 0){
return UITableViewAutomaticDimension
}
else if (indexPath.section == 1){
return 62
}
else {
print(height)
return CGFloat(height)
}
}
else{
if (indexPath.section == 0){
return UITableViewAutomaticDimension
}
else if (indexPath.section == 1){
return 62
}
else {
return 0
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.section == 0)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! AddressTableViewCell
tableDetails.isHidden = false
activityIndicator.stopAnimating()
let arr = shippingArray[indexPath.row]
cell.deleteButton.tag = indexPath.row
cell.nameLabel.text = arr["name"] as? String
cell.addressLabel.text = arr["address"]as? String
let mobilenumber : Any = arr["number"] as AnyObject
cell.mobileNumberLabel.text = "\(mobilenumber)"
cell.radioButton.tag = indexPath.row
cell.editButton.tag = indexPath.row
cell.deleteButton.tag = indexPath.row
cell.editButton.isHidden = true
cell.deleteButton.isHidden = true
cell.radioButton.addTarget(self, action: #selector(selectRadioButton(_:)), for: .touchUpInside)
cell.deleteButton.addTarget(self, action: #selector(deleteAction(button:)), for: .touchUpInside)
let checkIndex = self.checkIsRadioSelect.index(of: indexPath.row)
if(checkIndex != nil){
cell.radioButton.isSelected = true
cell.editButton.isHidden = false
cell.deleteButton.isHidden = false
}
else
{
cell.radioButton.isSelected = false
cell.editButton.isHidden = true
cell.deleteButton.isHidden = true
}
if (checkIsPaymentRadioSelect == true){
let defaultvalue = arr["default"] as! Int
if defaultvalue == 1 {
cell.radioButton.isSelected = true
cell.editButton.isHidden = false
cell.deleteButton.isHidden = false
addressSelected = true
tableDetails.tableFooterView?.isHidden = false
}
}
return cell
}
else if (indexPath.section == 1){
let cell = tableView.dequeueReusableCell(withIdentifier: "addresscell", for: indexPath) as! CreateNewAddressTableViewCell
cell.newAddressButton.addTarget(self, action: #selector(newAddressAction(_:)), for: .touchUpInside)
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "shippingmethodcell", for: indexPath) as! MethodTableViewCell
cell.delegate = self
cell.boolDelegate = self
cell.shippingTableView.reloadData()
if shippingRadio == true {
cell.select = shippingRadio
cell.boolSelected()
cell.shippingmethodURL()
cell.shippingTableView.reloadData()
}
else{
cell.select = methodRadio
cell.shippingTableView.reloadData()
}
return cell
}
}
in this cell class i had got the api data and is passed to table view as shown in the code now i need to call api during cell selection of address can anyone help me how to clear the error or any alternative for this
var chekIndex:IndexPath?
var arrayss = [String:Any]()
var keys = [String]()
let urlString = "http://www.json-generator.com/api/json/get/bVgbyVQGmq?indent=2"
var delegate: CheckoutDelegate?
var heightConstant: Int?
var name = [String]()
var totalCount = 0
var radioSelected:Bool?
var radioSelection: Bool?
var boolDelegate: BoolValidationDelegate?
var select:Bool?
override func awakeFromNib() {
super.awakeFromNib()
radioSelection = false
self.shippingmethodURL()
shippingTableView.delegate = self
shippingTableView.dataSource = self
shippingTableView.rowHeight = UITableViewAutomaticDimension
shippingTableView.estimatedRowHeight = shippingTableView.rowHeight
// Initialization code
}
func paymentRadioAction(button : KGRadioButton) {
_ = button.center
let centralPoint = button.superview?.convert(button.center, to:self.shippingTableView)
let indexPath = self.shippingTableView.indexPathForRow(at: centralPoint!)
if button.isSelected {
} else{
chekIndex = indexPath
radioSelection = true
self.shippingTableView.reloadData()
self.boolDelegate?.boolvalidation(bool: radioSelection!)
}
}
func shippingmethodURL() {
guard let url = URL(string: self.urlString) else {return}
URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) -> Void in
if let data = data, let jsonObj = (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)) as? [String:Any] {
self.arrayss = jsonObj
self.keys = Array(jsonObj.keys)
for value in jsonObj.values {
if let array = value as? [[String:Any]] {
for element in array {
if (element["name"] as? String) != nil {
self.totalCount += 1
}
}
}
}
DispatchQueue.main.async {
self.shippingTableView.reloadData()
let sectionHeight = self.arrayss.count * 31
let cellHeight = self.totalCount * 44
self.shippingHeightConstraint.constant = CGFloat(sectionHeight + cellHeight)
self.heightConstant = Int(self.shippingHeightConstraint.constant)
self.delegate?.heightConstant(int: self.heightConstant!)
}
}
}).resume()
}
func numberOfSections(in tableView: UITableView) -> Int {
return arrayss.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.keys[section]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let key = self.keys[section]
let a :[Any] = arrayss[key] as! [Any]
return a.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "shippingCell", for: indexPath) as! ShippingMethodTableViewCell
let key = self.keys[indexPath.section]
var a :[Any] = arrayss[key] as! [Any]
var dictionary = a[indexPath.row] as! [String:Any]
let name = dictionary["name"]
let price = dictionary ["price"]
cell.methodLabel.text = name as? String
cell.priceLabel.text = price as? String
cell.radioButton.addTarget(self, action: #selector(paymentRadioAction(button:)), for: .touchUpInside)
if chekIndex == indexPath {
cell.radioButton.isSelected = true
} else {
cell.radioButton.isSelected = false
}
return cell
}
and the first time image loading is shown below
!enter image description here ]1
and if i select another radio button in first section it was working fine as expected and image is shown below
I'm doing a SearchBar and when you type the word "nike" it has to display custom cells "shop Types" that has that name in their title but also the brand nike if it exists in the brands array.
For the moment I manage to get the correct numbers of rows in table view:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(searchActive) {
return filtered.count + filteredBrands.count
}
return shops.count;
}
Also managed to filter brands and shops arrays:
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
filtered = shopNames.filter({ (text) -> Bool in
let tmp: NSString = text
let range = tmp.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch)
return range.location != NSNotFound
})
filteredBrands = brandNames.filter({ (text) -> Bool in
let tmp: NSString = text
let range = tmp.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch)
return range.location != NSNotFound
})
if(filtered.count == 0 && filteredBrands.count == 0){
searchActive = false
} else {
searchActive = true
}
//user pressed the x button to clean content
if(searchText == "") {
searchActive = false
}
currentTableView.reloadData()
}
But now I'm stuck in tableView:cellForRow:atIndexPath() :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if(self.brandNamesCopyCounter > 0){
brandNamesCopyCounter -= 1
let cell = currentTableView.dequeueReusableCellWithIdentifier("brandCell") as! HomeSearchBrandCell
if(searchActive){
if(filteredBrands.count > indexPath.row){
cell.title.text = filteredBrands[indexPath.row]
if(cell.iconFacility != nil){
cell.iconFacility.image = UIImage.init(named: "brands.pdf")
}
}
} else {
print(indexPath.row)
if(brandsArray.count > indexPath.row){
cell.title.text = brandsArray[indexPath.row].name_en
}
if(cell.iconFacility != nil){
cell.iconFacility.image = UIImage.init(named: "brands.pdf")
}
}
return cell
}else{
let cell = currentTableView.dequeueReusableCellWithIdentifier("homeSearchCell") as! HomeSearchCell
if(searchActive){
if(filtered.count > indexPath.row){
print(indexPath.row)
cell.title.text = self.filtered[indexPath.row]
if(cell.iconFacility != nil){
cell.iconFacility.image = UIImage.init(named: "shops.pdf")
}
}
} else {
cell.title.text = shops[indexPath.row].name
if(cell.iconFacility != nil){
cell.iconFacility.image = UIImage.init(named: "shops.pdf")
}
}
return cell
}
}
I really don't have a clue on how to dequeue the correct cell and I'm having weird errors when reuse cells. Can anybody help?
I think that this example can help you
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(section == 0)
{
return filtered.count
}
return filteredBrands.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var tableCell2 : UITableViewCell
if(indexPath.section == 0)
{
tableCell2 = tableView.dequeueReusableCellWithIdentifier("ShopCell") as! ShopCell
}else
{
tableCell2 = tableView.dequeueReusableCellWithIdentifier("BrandsCell") as! BrandsCell
}
return tableCell2
}
I hope this helps you
I create a tableView inside a tableView inside another tableView. The problem is that when the cell is reused, the tableView inside another tableView is corrupted
I usually get that error
Invalid update: invalid number of rows in section 0. The number of
rows contained in an existing section after the update (1) must be
equal to the number of rows contained in that section before the
update (2), plus or minus the number of rows inserted or deleted from
that section (1 inserted, 0 deleted) and plus or minus the number of
rows moved into or out of that section (0 moved in, 0 moved out).'
. How to create a cell in swift without the system of reuse ?
any help will be appreciated :)
private(set) let kNAME_CELL_FIRST = "Cell1"
private(set) let kNAME_CELL_SECOND = "Cell2"
private(set) let kNAME_CELL_THIRD = "Cell3"
private (set) var indexSelectedFirst : NSIndexPath!
weak var currentTableViewSecond : UITableView!
enum IDTableView : Int {
case kFIRST = 0
case kSECOND = 1
case kTHIRD = 2
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch tableView.tag {
case IDTableView.kFIRST.rawValue:
return self.categories.count
case IDTableView.kSECOND.rawValue:
return self.getItemSECOND()
case IDTableView.kTHIRD.rawValue:
return 1
default: return 0
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
switch tableView.tag {
case IDTableView.FIRST.rawValue :
if self.idSelectedSecond != -1 && self.indexSelectedFirst != nil && indexPath == self.indexSelectedFirst {
return HEIGHT_CELL_FIRST + (HEIGHT_CELL_SECOND * CGFloat(self.getItemSecond())) + HEIGHT_CELL_THIRD
}
else if self.indexSelectedCat != nil && indexPath == self.indexSelectedCat {
return HEIGHT_CELL_FIRST + (HEIGHT_CELL_SECOND * CGFloat(self.getItemSecond()))
}
return HEIGHT_CELL_FIRST
case IDTableView.SECOND.rawValue :
if self.indexSelectedSecond != nil && self.indexSelectedSecond == indexPath {
return HEIGHT_CELL_SECOND + HEIGHT_CELL_THIRD
} else {
return HEIGHT_CELL_SECOND
}
case IDTableView.THIRD.rawValue:
return HEIGHT_CELL_THIRD
default:
return 0
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch tableView.tag {
case IDTableView.FIRST.rawValue:
return self.tableViewFirst(tableView, cellForRowAtIndexPath: indexPath)
case IDTableView.Second.rawValue:
return self.tableViewSecond(tableView, cellForRowAtIndexPath: indexPath)
case IDTableView.THIRD.rawValue:
return self.tableViewThird(tableView, cellForRowAtIndexPath: indexPath)
default:
return UITableViewCell()
}
}
func tableViewFirst(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : firstTableViewCell = tableView.dequeueReusableCellWithIdentifier(kNAME_CELL_CAT, forIndexPath: indexPath) as firstTableViewCell
let item = self.arrFirst[indexPath.row]
cell.labelName.text = item.name
cell.imageView_.image = UIImage(named: "test.jpg")
cell.imageView_.clipsToBounds = true
cell.selectionStyle = UITableViewCellSelectionStyle.None;
if self.indexSelectedFirst != nil && indexPath == self.indexSelectedFirst {
cell.labelName.textColor = UIColor(rgb: 0xf1d3b6)
cell.tableViewSecond.hidden = false
} else {
cell.labelName.textColor = UIColor(rgb: 0xf1d3b6)
cell.tableViewSecond.hidden = true
}
return cell
}
func tableViewSecond(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : SecondTableViewCell = tableView.dequeueReusableCellWithIdentifier(kNAME_CELL_SECOND) as SecondTableViewCell
let itemParent = self.getSelectedItemFirstArray()!
let item = itemParent.arraySecond[indexPath.row]
cell.labelName.text = item.name
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
func tableViewThird(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : ThirdTableViewCell = tableView.dequeueReusableCellWithIdentifier(kNAME_CELL_THIRD) as ThirdTableViewCell
cell.labelKey.text = "test string"
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
switch tableView.tag {
case IDTableView.FIRST.rawValue:
self.tableViewFirst(tableView, didSelectRowAtIndexPath: indexPath)
default:break
}
}
func tableViewFirst(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let lastIndexPath = self.indexSelectedFirst
self.closeCurrentSelectedItemK1 { (over: Bool) -> Void in
// JUST NEED TO CLOSE THE ITEM
if lastIndexPath != nil && lastIndexPath == indexPath { return }
let cell = tableView.cellForRowAtIndexPath(indexPath) as FirstTableViewCell
self.indexSelectedFirst = indexPath
self.currentTableSecond = cell.tableViewSecond
cell.tableViewSecond.hidden = false
UIView.animateWithDuration(0.6, animations: { () -> Void in
var indexPaths = [NSIndexPath]()
CATransaction.begin()
CATransaction.setCompletionBlock({ () -> Void in
self.tableViewFirst.beginUpdates()
self.tableViewFirst.endUpdates()
})
self.currentTableViewSecond.beginUpdates()
for i in 0...(self.getItemSecond()-1) {
indexPaths.append(NSIndexPath(forRow: i, inSection: 0))
}
self.currentTableViewSecond.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Bottom)
self.currentTableViewSecond.endUpdates()
CATransaction.commit()
}, completion: { (over: Bool) -> Void in })
}
}
func closeCurrentSelectedItemK1(completion : ((Bool)-> Void)) -> Void{
if self.indexSelectedFirst != nil {
var indexPaths = [NSIndexPath]()
UIView.animateWithDuration(0.3, animations: { () -> Void in
let cell = self.tableViewFirst.cellForRowAtIndexPath(self.indexSelectedFirst) as FirstTableViewCell
CATransaction.begin()
self.currentTableViewSecond.beginUpdates()
self.currentTableViewSecond.hidden = true
for i in 0...self.currentTableViewSecond.numberOfRowsInSection(0)-1 {
indexPaths.append(NSIndexPath(forRow: i, inSection: 0))
}
self.indexSelectedFirst = nil
/* IF SOME TO REMOVE */
if indexPaths.count > 0 {
self.currentTableViewSecond.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Top)
}
self.currentTableViewSecond.endUpdates()
CATransaction.setCompletionBlock({ () -> Void in
self.tableViewFirst.beginUpdates()
self.tableViewFirst.endUpdates()
self.currentTableViewSecond = nil
self.indexSelectedFirst = nil
completion(true)
})
CATransaction.commit()
} else {
completion(true)
}
}