I am not able to wrap my head around the implementation of sections in cellForRowAtIndexPath.
I have a UITableView in which I would like to show 2 sections.
Incoming Friend Requests
Friends
In Storyboard, I change my UITableView Style to Grouped.
Next, I would like there to be no Friend Request section if there are no friend requests. In viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
(...)
if friendRequests.isEmpty {
friendsDataSource = friends
} else {
friendsDataSource = [friendRequests, friends]
}
}
The rest:
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return friendsDataSource.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friendsDataSource[section].count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let friendRequest = friendsDataSource[0][indexPath.row]
let friend = friendsDataSource[1][indexPath.row]
if let cell = tableView.dequeueReusableCellWithIdentifier("FriendCell") as? FriendCell {
cell.configureProfileCell(userProfile)
return cell
} else {
return FriendCell()
}
}
I know my cellForRowAtIndexPath is disgusting but I have absolutely no idea how to implement it.
Any help in the right direction, greatly appreciated
Discovered if (indexPath.section == 0), and I just hacked around that.
My eyes hurt looking at this so Please post better ways of doing this. For now:
var friendRequests = [FriendRequest]()
var friends = [UserProfile]()
var friendsDataSource = []
override func viewDidLoad() {
super.viewDidLoad()
friends = FriendManager.instance.myFriends
friendRequests = FriendManager.instance.incomingFriendRequests
if friendRequests.isEmpty {
friendsDataSource = [friends]
} else {
friendsDataSource = [friendRequests, friends]
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return friendsDataSource.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friendsDataSource[section].count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("FriendCell", forIndexPath: indexPath) as? FriendCell {
if friendRequests.isEmpty {
let friendCell = friends[indexPath.row]
cell.configureProfileCell(friendCell)
} else {
if (indexPath.section == 0) {
let friendRequestCell = friendRequests[indexPath.row]
cell.configureRequestCell(friendRequestCell)
} else if (indexPath.section == 1) {
let friendCell = friends[indexPath.row]
cell.configureProfileCell(friendCell)
}
}
return cell
} else {
return FriendCell()
}
}
You should use the other, newer dequeueing method: dequeReusableCellWithIdentifier(_:forIndexPath:) instead (passing the actual index path).
That one is guaranteed to always succeed, so you can do without this if/else structure:
if let cell = ... {
...
return cell
}
else {
return FriendCell()
}
By the way, you are returning the FriendCell instance fresh, without configuring it. Is that what you really want?
Clarification
The method dequeReusableCellWithIdentifier(:) succeeds only if there is one or more cells with the specified identifier already enqueued for reuse; the first few times you call it it will return nil and you need to fallback to instantiating a new cell (with the same identifier), for immediate use (and later reuse):
func tableView(tableView:UITableView, cellForRowAtIndexPath:NSIndexPath) -> UITableViewCell
{
if let cell = tableView.dequeReusableCellWithIdentifier("Identifier") as? FriendCell {
// Successfully dequeued for reuse;
// configure it:
// (set labels' texts, etc.)
return cell
}
else{
// No cell enqueued; create anew
let cell = FriendCell(style:.Plain, reuseIdentifier:"Identifier")
// configure it
// (set labels' texts, etc.)
return cell
}
}
...But because this check is a pain, Apple added a new method:
dequeReusableCellWithIdentifier(identifier:String, forIndexPath:NSIndexPath)
that internally performs the dequeueing and also initializes a new cell if no one is available. This eliminates the need for an else path in the code above, and it gets smarter:
func tableView(tableView:UITableView, cellForRowAtIndexPath:NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeReusableCellWithIdentifier("Identifier", forIndexPath:indexPath) as! FriendCell
// (Never fails - provided identifier is right and class is registered for it)
// configure it:
// (set labels' texts, etc.)
return cell
}
Related
I have an app with an UITableView and its data gets updated regularly. If the data receives new element, the table view is reloaded. Let’s say the table view has place for 10 visible cells, but data for only 2 of them. The user has scrolled in either direction and not released the table view from touch. If the user has scrolled up, they may have hidden the first cell and only the second one would be visible. Then a new element is received and reloadData is called. Instead of waiting for releasing the table view to update, the tableview gets updated right away and the contentOffset is reset to 0. The tableView just resets to start position while the user has scrolled and not released.
I tried similar setup in separate Xcode project and the issue does not appear there. I wonder what the difference could be.
This is some of the code:
For the ViewController that is the dataSource:
func viewDidLoad() {
super.viewDidLoad()
//some other code
DataManager.shared.onElementReceival = { [weak self] in
guard let self = self else { return }
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return DataManager.shared.data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
cell.textLabel?.textColor = .white
cell.backgroundColor = .clear
if let name = DataManager.shared.data[indexPath.row].name {
cell.textLabel?.text = name
} else {
cell.textLabel?.text = "Unnamed"
}
return cell
}
From the DataManager
func didReceive(_ element: Element) {
data.append(element)
onElementReceival()
}
Try to reload only cell using:
tableview.insertRowsAtIndexPaths([IndexPath(forRow: Yourarray.count-1, inSection: 0)], withRowAnimation: .Automatic)
In your example:
let onElementReceival:(IndexPath) -> Void = { [weak self] inx in
guard let self = self else { return }
DispatchQueue.main.async {
tablview.beginUpdates()
tablview.insertRowsAtIndexPaths(at: [inx], with: .automatic)
tablview.endUpdates()
}
}
From DataManager
func didReceive(_ element: Element) {
data.append(element)
onElementReceival(IndexPath(row:data.count, section: 0))
}
I am trying to create a contacts page where you can see all your contacts with a friend request cell showing up when you receive a friend request, but not there when you do not have any. At the moment, both custom cells work fine. The issue I have is that the contactRequestTableViewCell overlaps the first cell of the contactListTableViewCell.
I have researched other questions about two custom tableviewcells and none are quite having the same issues that I am facing.
Here is my executing code at the moment, I am returning 2 sections in the table view.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! ContactListTableViewCell
let requestCell = tableView.dequeueReusableCellWithIdentifier("requestCell", forIndexPath: indexPath) as! ContactRequestsTableViewCell
let user = OneRoster.userFromRosterAtIndexPath(indexPath: indexPath)
if (amountOfBuddyRequests > 0) {
if (indexPath.section == 0) {
requestCell.hidden = false
cell.hidden = false
requestCell.friendRequestLabel.text = "test"
} else if (indexPath.section >= 1) {
cell.contactNameLabel!.text = user.displayName;
cell.contactHandleLabel!.text = "# " + beautifyJID(user.jidStr)
cell.contactHandleLabel!.textColor = UIColor.grayColor()
OneChat.sharedInstance.configurePhotoForImageView(cell.imageView!, user: user)
}
return cell;
}
else { // if buddy requests == 0
requestCell.hidden = true
cell.contactNameLabel!.text = user.displayName;
cell.contactHandleLabel!.text = "# " + beautifyJID(user.jidStr)
cell.contactHandleLabel!.textColor = UIColor.grayColor()
print ("This is how many unreadMessages it has \(user.unreadMessages)")
// If there is unread messages for a person highlight it blue
// However this feature isn't working right now due to unreadMessages bug
if user.unreadMessages.intValue > 0 {
cell.backgroundColor = .blueColor()
} else {
cell.backgroundColor = .whiteColor()
}
OneChat.sharedInstance.configurePhotoForCell(cell, user: user)
return cell;
}
}
This is the current output that I have right now, my cells that have "test" are covering up other contactListTableViewCells.
The function tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell should always return one and the only one TableViewCell you want at indexPath, so you don't want to always return cell of type ContactListTableViewCell.
According to documentation, the cellForRowAtIndexPath tableView method asks for the cell at the indexPath, which means literally there can only be one cell at certain row of a certain section, so returning two cells is not an option.
I suggest you use two arrays to store the requests and contacts information. For example, you have arrays requests and contacts. Then you can tell the tableView how many rows you want:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return requests.count + contacts.count
}
and then in cellForRowAtIndexpath you do something like:
override func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row < requests.count {
// return a request cell
}
else {
// return a contact cell
}
}
I'm only using one tableView section here. If you still want two sections you can simply return 2 in numberOfSections function and add if statements in cellForRowAtIndexPath for indexPath.section.
Hope this helps.
It turns out that the issue was dealing with the data sources. My data sources were not pointing to the correct tableviewcell. This resulted in them pointing to an incorrect cell. This issue was fixed by remaking the data sources system that was in place. This issue will not affect most as the data sources will point to the correct tableviewcell by default.
Contrary to what another poster said, you can in fact display two or more custom cells in a single table. This is how I fixed the tableView display issues:
var friendRequests = ["FriendRequest1", "FriendRequest2"]
var contacts = ["User1","User2","User3","User4"]
var amountOfBuddyRequests = 1
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if (amountOfBuddyRequests > 0) {
return 2
}
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (amountOfBuddyRequests > 0) {
if (section == 0) {
return friendRequests.count
}
}
return contacts.count
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (amountOfBuddyRequests > 0) {
if (indexPath.section == 0) {
let requestCell = tableView.dequeueReusableCellWithIdentifier("requestCell") as! ContactRequestsTableViewCell
requestCell.friendRequestLabel.text = friendRequests[indexPath.row]
requestCell.onButtonTapped = {
self.friendRequests.removeAtIndex(indexPath.row)
self.tableView.reloadData()
}
requestCell.addButtonTapped = {
self.addUser(self.friendRequests[indexPath.row])
self.friendRequests.removeAtIndex(indexPath.row)
self.tableView.reloadData()
}
return requestCell
}
}
let friendCell = tableView.dequeueReusableCellWithIdentifier("FriendCell") as! ContactListTableViewCell
friendCell.contactNameLabel.text = contacts[indexPath.row]
return friendCell
}
So I have a Segmented Control that switches between 2 TableViews & 1 MapView inside a MainVC.
I'm able to switch the views in the simulator by adding an IBAction func changedInSegmentedControl to switch which views are hidden while one of them is active.
I created 2 custom TableViewCells with XIBs. I also added tags with each TableView.
My question is how do I add them in cellForRowAtIndexPath?
Currently, my code is:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
// var cell: UITableViewCell
if (tableView.tag == 1) {
cell: CardTableViewCell = tableView.dequeueReusableCellWithIdentifier("CardCell") as! CardTableViewCell
return cell
}
else if (tableView.tag == 2) {
cell: ListTableViewCell = tableView.dequeueReusableCellWithIdentifier("ListCell") as! ListTableViewCell
return cell
}
}
Of course Swift requires a "return cell" for the function outside the If statements. I tried with a var cell: UITableViewCell outside, but run into trouble finishing the dequeuReusableCellWithIdentifier.
Anyone have some idea how to do this? Thanks.
This is how I approached it (Swift 3.0 on iOS 10). I made one tableView with two custom cells (each is their own subclass). The segmented control is on my navigationBar and has two segments: People and Places.
There are two arrays within my class, (people and places) which are the data sources for the two table views. An action attached to the segmentedControl triggers the reload of the table, and the switch statement in cellForRowAtIndex controls which cell from which array is loaded.
I load data into the two data arrays from an API call, the asynchronous completion of which triggers dataLoaded(), which reloads the tableView. Again I don't have to worry about which segment is selected when the table is reloaded: cellForRowAtIndex takes care of loading the correct data.
I initialize a basic cell just as UITableViewCell and then in the case statement I created and configure the custom cell. Then I return my custom type cell at the end, and as long as the reuseIdentifiers and classes are correct in cellForRowAtIndex, the correct cell is initialized and displayed in the tableView.
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var peoplePlacesControl: UISegmentedControl!
fileprivate var placesArray: [PlaceModel]?
fileprivate var usersArray: [UserModel]?
#IBAction func segmentedControlActionChanged(_ sender: AnyObject) {
tableView.reloadData()
switch segmentedControl.selectedSegmentIndex {
case 0:
loadUsersfromAPI()
case 1:
loadPlacesFromAPI()
default:
// shouldnt get here
return
}
}
func dataLoaded() {
switch peoplePlacesControl.selectedSegmentIndex {
case 0: // users
if favoriteUsersArray == nil {
self.tableView.reloadData()
} else {
hideTableViewWhileEmpty()
}
case 1: // places
if placesArray != nil {
self.tableView.reloadData()
} else {
hideTableViewWhileEmpty()
}
default:
return
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch segmentedControl.selectedSegmentIndex {
case 0:
if usersArray != nil {
return usersArray!.count
} else {
return 0
}
case 1: // places
if placesArray != nil {
return placesArray!.count
} else {
return 0
}
default:
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell()
switch peoplePlacesControl.selectedSegmentIndex {
case 0: // people
let userCell = tableView.dequeueReusableCell(withIdentifier: "MyUserCell", for: indexPath) as! MyUserTableViewCell
if usersArray != nil && indexPath.row < usersArray!.count {
let user = usersArray![indexPath.row]
userCell.configure(user)
userCell.myDelegate = self
}
cell = userCell as MyUserTableViewCell
case 1: // places
let placeCell = tableView.dequeueReusableCell(withIdentifier: "MyPlaceCell", for: indexPath) as! MyPlaceTableViewCell
if favoritePlacesArray != nil && indexPath.row < favoritePlacesArray!.count {
let place = placesArray![indexPath.row]
placeCell.configure(place)
placeCell.myDelegate = self
}
cell = placeCell as MyPlaceTableViewCell
default:
break
}
return cell
}
I have made change in your code.
Use following code
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
if (tableView.tag == 1) {
cell: CardTableViewCell = tableView.dequeueReusableCellWithIdentifier("CardCell") as! CardTableViewCell
return cell
}
cell: ListTableViewCell = tableView.dequeueReusableCellWithIdentifier("ListCell") as! ListTableViewCell
return cell
}
I am using parse.com framework with Swift and in PFQueryTableViewController when I set the pagination it won't work. If the DB has less rows than the number set in objectPerPage it works fine, but if there are more rows and when I run the app it keeps showing the loading screen and nothing is downloaded, when I do "swipe as refresh" it crash as
Error
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 5 beyond bounds [0 .. 4]
ImagesTableViewController.swift
import UIKit
import Parse
import ParseUI
import Bolts
class ImagesTableViewController: PFQueryTableViewController {
#IBAction func unwindToSegue (segue : UIStoryboardSegue) {}
// Initialise the PFQueryTable tableview
override init(style: UITableViewStyle, className: String!) {
super.init(style: style, className: className)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Configure the PFQueryTableView
self.parseClassName = "Image"
self.pullToRefreshEnabled = true
self.paginationEnabled = true
self.objectsPerPage = 5
}
// Define the query that will provide the data for the table view
override func queryForTable() -> PFQuery {
var query = PFQuery(className: "Image")
query.whereKey("deleted", notEqualTo: 1)
query.orderByDescending("createdAt")
return query
}
//override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("ImageCell") as! ImageTVCell!
if cell == nil {
cell = ImageTVCell(style: UITableViewCellStyle.Default, reuseIdentifier: "ImageCell")
}
// Extract values from the PFObject to display in the table cell HEADLINE
if let caption = object?["caption"] as? String {
cell?.headlineLabel?.text = caption
}
// Display image
var initialThumbnail = UIImage(named: "question")
cell.postImageView.image = initialThumbnail
if let thumbnail = object?["image"] as? PFFile {
cell.postImageView.file = thumbnail
cell.postImageView.loadInBackground()
}
return cell
}
// if I remove this code pagination work but the cell height is wrong
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return calculateHeightForRowAtIndexPath(indexPath)
}
func calculateHeightForRowAtIndexPath(indexPath: NSIndexPath) -> CGFloat {
if let ratio = objectAtIndexPath(indexPath)?["aspect"] as? Float {
println("Ratio: \(ratio)")
return tableView.bounds.size.width / CGFloat(ratio)
} else {
return 50.0
}
}
#IBAction func addNewPhotoButton(sender: UIBarButtonItem) {
self.tabBarController?.tabBar.hidden = true
self.performSegueWithIdentifier("showUploadNewImage", sender: self)
}
}
This problem occurs because of PFQueryTableViewController's implementation of the method tableView:numberOfRowsInSection from the UITableViewDataSource. I've copy/pasted it from the GitHub repo containing PFQueryTableViewController.m
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger count = [self.objects count];
if ([self _shouldShowPaginationCell]) {
count += 1;
}
return count;
}
It simply returns the count of objects to display (which makes sense), but if pagination is enabled, then it requires for an extra cell to be shown. This means you have to manually created another cell with the text "Load more data" or something like that, which would trigger a refresh.
A way to overcome this is simply by overriding tableView:numberOfRowsInSection yourself with the following:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.objects!.count
}
UPDATE 1
The prebuilt Parse pagination button was gone in previous answer
Use the following code snippet for calculating the height of the cells to display the prebuilt Parse pagination button
func calculateHeightForRowAtIndexPath(indexPath: NSIndexPath) -> CGFloat {
// Special case for pagination, using the pre-built one by Parse
if (indexPath.row >= objects!.count) { return 50.0 }
// Determines the height if an image ratio is present
if let ratio = objectAtIndexPath(indexPath)?["aspect"] as? Float {
println("Ratio: \(ratio)")
return tableView.bounds.size.width / CGFloat(ratio)
} else {
return 50.0
}
}
Using Parse 1.11 with iOS 9.2 and Xcode 7.2 Parse Pagination works perfectly.
Problems surface when the user override some funcs used by Parse itself without properly managing the "Load More ..." row added by Parse.
In my case I needed to override tableView-canEditRowAtIndexPath to determine whether the current user can or cannot delete the row according to the object's ACL.
My initial func was:
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
if let curUser = PFUser.currentUser() {
let currentObject = objects![indexPath.row]
if let acl = currentObject.ACL {
return acl.getWriteAccessForUser(curUser)
} else {
return true
}
}
return true
}
but I got the exception of indexpath out of bounds when the Load More line was met during list scrolling.
Problem was solved adding this test:
if (indexPath.row == self.objects!.count) { // row "Load More ..."
return true
}
Without this code the "Load More ..." row was not added by Parse!!
So the complete correct overriding func is:
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
if (indexPath.row == self.objects!.count) { // row "Load More ..."
return true
}
if let curUser = PFUser.currentUser() {
let currentObject = objects![indexPath.row]
if let acl = currentObject.ACL {
return acl.getWriteAccessForUser(curUser)
} else {
return true
}
}
return true
}
Generally speaking all overridden funcs including heightForRowAtIndexpath, must take care of the extra line added by Parse when pagination is enabled.
HTH
Roberto Targa
I want to filter a tableview based on a String value set by the user.
The tableview is generated with a Cell consisting of several components (Images, labels, etc.) all which is located in a "mess" of Arrays, and Dictionaries. I am using below code. When I filter i just hide the cells, but they are still taking up space in the tableview.
What is the best way to apply the filter and thereby only get the cells which comply?
The field which I'm filtering on is the topic.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if filterChoice == "Alle"
{
var cell = tableView.dequeueReusableCellWithIdentifier("NewsCell") as News_TableViewCell
cell.subject!.text = subjects[indexPath.row]
if videoEmbedCode.valueForKey(self.recordIDs[indexPath.row]) != nil
{
var iframeCode: NSString = videoEmbedCode.valueForKey(self.recordIDs[indexPath.row]) as String
var html = "<html><body>\(iframeCode)</body></html>"
cell.webView.loadHTMLString(html, baseURL :nil)
cell.webView.hidden = false
} else if self.imageCache.valueForKey(self.recordIDs[indexPath.row]) != nil
{
cell.newsImage.image = self.imageCache.valueForKey(self.recordIDs[indexPath.row]) as? UIImage
cell.newsImage.hidden = false
} else
{
}
if topics.valueForKey(self.recordIDs[indexPath.row]) != nil
{
cell.topicLabel.text = topics.valueForKey(self.recordIDs[indexPath.row]) as? String
}
else
{
cell.topicLabel.text = ""
}
return cell
}
else if topics.valueForKey(self.recordIDs[indexPath.row]) as? String == filterChoice
{
//Only return cells with specified topic
var cell = tableView.dequeueReusableCellWithIdentifier("NewsCell") as News_TableViewCell
if topics.valueForKey(self.recordIDs[indexPath.row]) != nil
{
cell.topicLabel.text = topics.valueForKey(self.recordIDs[indexPath.row]) as? String
}
else
{
cell.topicLabel.text = ""
}
cell.subject!.text = subjects[indexPath.row]
if videoEmbedCode.valueForKey(self.recordIDs[indexPath.row]) != nil
{
var iframeCode: NSString = videoEmbedCode.valueForKey(self.recordIDs[indexPath.row]) as String
var html = "<html><body>\(iframeCode)</body></html>"
cell.webView.loadHTMLString(html, baseURL :nil)
cell.webView.hidden = false
} else if self.imageCache.valueForKey(self.recordIDs[indexPath.row]) != nil
{
cell.newsImage.image = self.imageCache.valueForKey(self.recordIDs[indexPath.row]) as? UIImage
cell.newsImage.hidden = false
} else
{
}
return cell
}
else
{
var cell = UITableViewCell()
cell.hidden = true
return cell
}
}
You need to track number of cells in DataSource method, that asks you about number of cells for current section.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Here
}
If you just do cell.hidden = true space won't disappear, it will just hide content. It's because all cells layout calculated in other methods.
If you want to hide space for such cells you could choose several solution:
(Best) calculate number of non hidden cells, return new count in func tableView(tableView: UITableView, numberOfRowsInSection section: Int) and recalculate dictionary with info for func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
It not good solution, but for your problem can take place. In method, that asked for tableView cells height func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat just return 0 for hidden cells.