Hide and Expand UITableView Sections - ios

I am trying to implement a feature where if a user clicks on one of the items in my UICollectionView (that is embedded within a UITableViewCell) it causes another section to appear in the UITableView (below the UICollectionView) with information about that item. I'm having problems with the height of the section that is to appear when clicked. If I set it to 0 initially in heightForRowAtIndexPath there doesn't seem to be a way to alter the cells height later on. I tried giving the cell an initial height then hiding it with cell.hidden but that still leaves the section visible. Maybe there is an alternative way to do this, but after a lot of googling i'm coming up short.
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.section == 3 {
return 90
}
else if indexPath.section == 1 {
return 185
}
else if indexPath.section == 2 {
return 155
}
else if indexPath.section == 4 {
return 100
}
return UITableViewAutomaticDimension
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
}
else if indexPath.section == 3 {
let cell = tableView.dequeueReusableCellWithIdentifier("weeklyCell", forIndexPath: indexPath) as! WxTableViewCell
cell.selectionStyle = UITableViewCellSelectionStyle.None
cell.forecastCollectionView.delegate = self
cell.forecastCollectionView.dataSource = self
cell.forecastCollectionView.reloadData()
cell.forecastCollectionView.tag = indexPath.row
cell.forecastCollectionView.showsHorizontalScrollIndicator = false
return cell
}
else if indexPath.section == 4 {
let cell = tableView.dequeueReusableCellWithIdentifier("dailyInformation", forIndexPath: indexPath) as! WxTableViewCell
cell.hidden = true
cell.contentView.hidden = true
return cell
}
...
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let index = NSIndexPath(forRow: 0, inSection: 3)
let myindex = NSIndexPath(forRow: 0, inSection: 4)
var myTable = self.tableView
var lastCell = myTable.cellForRowAtIndexPath(index)
var dailyCell = myTable.cellForRowAtIndexPath(myindex)
var myView = UIView(frame: CGRectMake(20, 0, lastCell!.frame.width, lastCell!.frame.height))
dailyCell.frame.size = myView.frame.size
}

Never use indexPath numbers directly to address cells and sections in a switch/case. It's hard to handle when you want add/remove a row/section in the middle. Instead create an enum which have all sections and use an array that contains sections to be shown. You can have cell or section properties like cell identifiers or height in that enum.
enum FileDetailsCell: Printable {
case FileName, Location, Size
var description: String {
switch self {
case .FileName: return "noname"
case .Location: return"Location"
case .Size: return "Size"
}
}
var defaultValue: String {
switch self {
case .Location: return "/"
case .Size: return "Unknown"
default: return ""
}
}
var cellIdentifier: String {
switch self {
case .FileName: return "fileNameCell"
default: return "detailCell"
}
}
}
Here I use description for Cell title. defaultValue for initial value of cell and cellIdentifier is obvious for which purpose.
Then define an array which contains active cells:
var cellsArray: [FileDetailsCell] = [.FileName, .Size]
you can modify this array when you want change visible cells. Now, Location cell is hidden but you can make it visible later only by appending ".Location" to this array and reload tableView.
For tableview delegate:
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellsArray.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellInfo = cellsArray[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier(cellInfo.cellIdentifier, forIndexPath: indexPath) as! UITableViewCell
switch cellInfo {
case .FileName:
cell.textLabel?.text = item.fileName;
case .Location:
cell.textLabel?.text = cellInfo.description
cell.detailTextLabel?.attributedText = item.locationFormatted ?? cellInfo.defaultValue;
case .Size:
cell.textLabel?.text = cellInfo.description
cell.detailTextLabel!.text = item.size ?? cellInfo.defaultValue;
}
return cell
}
In this example I assumed sections are only one to avoid confusion. You can get similar approach for sections.

Related

Control multiple tableviews in a single view

I have a table view nested in a collection view and i'm returning 3 (possibly more in the future) collection view cells and I was wondering if it is possible to present different content in each one of the collection cells? I attached a few screenshots to better understand what I am taking about. Thanks.
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 3
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
cell.textLabel?.text = "Homeroom"
cell.detailTextLabel?.text = "8:15 AM - 9:00 AM"
cell.selectionStyle = .None
return cell
}
Yes you can. You need set a property for every tableView you have and in delegate method compare it like below
class Some: UIViewController {
var firstTableView: UITableView
var secondTableView: UITableView
override func viewDidLoad() {
firstTableView = YOUR_FIRST
secondTableView = YOUR_Second
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if tableView == firstTableView {
return 2;
}
else if tableView == secondTableView {
return 1;
}
return 3
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if tableView == firstTableView {
return 2;
}
else if tableView == secondTableView {
return 1;
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
if tableView == firstTableView {
cell = tableView.dequeueReusableCellWithIdentifier("cellOfFirstTableView", forIndexPath: indexPath) as! UITableViewCell
}
else if tableView == secondTableView {
cell = tableView.dequeueReusableCellWithIdentifier("cellOfSecondTableView", forIndexPath: indexPath) as! UITableViewCell
}
// Configure the cell...
if tableView == firstTableView {
cell.textLabel?.text = "Homeroom"
cell.detailTextLabel?.text = "8:15 AM - 9:00 AM"
cell.selectionStyle = .None
}
else if tableView == secondTableView {
cell.textLabel?.text = "Homeroom"
cell.detailTextLabel?.text = "8:15 AM - 9:00 AM"
cell.selectionStyle = .None
}
return cell
}
}
You can use UITableViewDelegate / UITableViewDataSource methods with if else conditions or some thing similar
eg.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == table1 { // table1 is a global var for the table
} else if tableView == table2 {
}
}
But I think it will be very clear if you use separate controller class for each table so you can easily manage the code.
But this depends on what type of data you have. If data is completely unrelated you can just use 3 different controllers.
Or if you can reuse data and codes among 3 tables then you can decide if you wanna use 3 different controllers or to use i class with above method.
eg.
let table1Controller = Table1Controller(dataList1)
let table2Controller = Table2Controller(dataList2)
let table3Controller = Table3Controller(dataList3)
table1.delegate = table1Controller
table1.dataSource = table1Controller
table2.delegate = table2Controller
table2.dataSource = table2Controller
table3.delegate = table3Controller
table3.dataSource = table3Controller

How to implement 2 UITableViews in 1 ViewController? The tableViews each have their own custom cell

I have 2 tableviews each with their own custom cell. How can I implement these tableviews in 1 viewcontroller? Here is my code.
Xcode can't cast the custom cell to the standard UITableViewCell
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// let accountSize = Int(accounts.count())
// return accountSize;
var count:Int?
if tableView == self.tableView {
count = Int(accounts.count())
}
if tableView == self.tableViewLoan {
count = Int(loans.count())
}
return count!
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// return UITableViewCell()
var cell:UITableViewCell?
if tableView == self.tableView {
//cell = tableView.dequeueReusableCellWithIdentifier("AccountsTableViewCell", forIndexPath: indexPath) as! AccountsTableViewCell
var cell1 = tableView.dequeueReusableCellWithIdentifier("AccountsTableViewCell", forIndexPath: indexPath) as! AccountsTableViewCell
var account:SUVTISDDA = SUVTISDDA()
account = accounts.objectAtIndex(UInt(indexPath.row))
var accountType = ""
var accountNum = ""
let accountNumber = account.Description.Value
var accountSplit = accountNumber.componentsSeparatedByString(": ")
if(accountSplit.count > 1){
accountNum = accountSplit[1]
accountType = accountSplit[0]
print("Account number: \(accountSplit[1])")
} else {
accountType = account.Description.Value
accountNum = " No Account Number"
print("Account number unavailable")
}
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
cell1.AccountTypeLabel!.text = accountType
cell1.AccountNumberLabel!.text = accountNum
cell1.AccountCurrencyLabel!.text = account.Currency.Value
cell1.AccountAmountLabel!.text = formatter.stringFromNumber(account.AvailableBalance.Value)
cell1.AccountTypeLabel!.textColor = UIColor(red:0, green:0.451, blue:0.682, alpha:1)
return cell1
}
if tableView == self.tableViewLoan {
var cell2 = tableView.dequeueReusableCellWithIdentifier("loanCell", forIndexPath: indexPath) as! LoanCell
var loan: SUVTILN = SUVTILN();
loan = loans.objectAtIndex(UInt(indexPath.row));
cell2.descriptionLabel.text = loan.Description.Value;
cell2.currencyLabel.text = loan.Currency.Value;
cell2.[![valueLabel][1]][1].text = loan.LoanAmount.Value.stringValue;
cell = cell2;
}
return cell;
}
http://i.stack.imgur.com/An6G2.png
Here is how you would configure two tableViews in one viewController (this can work with 2 or more tableViews).
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == yourFirstTableView {
return yourFirstArray.count
} else {
return yourSecondArray.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if tableView == yourFirstTableView {
let cell1 = tableView.dequeueReusableCellWithIdentifier("yourFirstCellReuseIdentifier", forIndexPath: indexPath) as! YourFirstCustomTableViewCell
// Configure the cell for your first tableView
return cell1
} else {
let cell2 = tableView.dequeueReusableCellWithIdentifier("yourSecondCellReuseIdentifier", forIndexPath: indexPath) as! YourSecondCustomTableViewCell
// Configure the cell for your second tableView
return cell2
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if tableView == studentsTableView {
// Perfrom action for your first tableView
} else {
// Perfrom action for your second tableView
}
}
I am not exactly sure what you are trying to do. Maybe try using a ContainerViewController and embedding two TableViewControllers into that. This should give the effect of having two TableViews in one ViewController but you will have separate files for each TableViewController.

Display show more in uitableview

By default each section should show maximum three cells. If any cell contains more than three cells, it should display 'show more' option.If show more is tapped I want to display the rest of the cells in that particular section. Two days I spent, nothing worked out.Segments at the top of the table. Depend on segment selected, tableview load the cells. This code for each segment each section varies, so the func cellForRowAtIndexPath: becomes big seriously very big. I roughly added code. This code is what i have tried.
if segmentName == .Feature || segmentName == .Services
{
if indexPath.section == 0
{
if boolShowFullFeature[indexPath.section] == false
{
if indexPath.row == showCells
{
return createShowMoreCell(indexPath.section)
}
else
{
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! SearchListViewCell
var firstTitle = "", secondTitle = "", thirdTitle = "", recomendValue = "", starValue = ""
(firstTitle, secondTitle, thirdTitle, recomendValue, starValue) = model.foodValue[indexPath.row]
cell.configureCell(firstTitle, secondTitle: secondTitle, thirdTitle: thirdTitle, recomendValue: recomendValue, starValue: starValue)
return cell
i had actually done this before. here is sample code:
var objects = [["1","2","3"],["q","w","e","r","t","y"],["z","x","c","v","b","n","m"]]
var sec = ["sec a","sec b","sec c"]
var showallSec = 0
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sec.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let items = objects[section].count
if items > 3{
if section == showallSec-1{
return items
}
return 4
}
return items
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let object = objects[indexPath.section][indexPath.row]
cell.textLabel!.text = object
if(indexPath.section != showallSec-1){
if(indexPath.row == 3){
cell.textLabel!.text = "show more"
}}
return cell
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sec[section]
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if(indexPath.row == 3){
showallSec = indexPath.section + 1
tableView.reloadData()
}
}

Dynamic Dimension of UITableViewCell ok, but how hide them if empty?

I have a UITableView with 3 prototyped cells (ex. 1st cell: image, 2nd cell: Description, 3. Links,...).
I would like to hide them if for a cell the data from the backend is empty (Ex. if there is no image, hide the first cell). In order to do that, I have override the heightForRowAtIndexPath function in this way:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
switch (indexPath.row) {
case 0:
if event?.photo_urls.count == 0{
return 0
}
else{
return 80.0
}
case 1:
if event?.description == ""{
return 0
}
else{
return 90.0
}
default:
return 100.0
}
}
and hidden the cell by doing
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch (indexPath.row) {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier("PhotoCell", forIndexPath: indexPath) as! UITableViewCell
if event?.photo_urls.count != 0 {
// ...
}
else{
cell.hidden = true
}
return cell
case 1:
let cell = tableView.dequeueReusableCellWithIdentifier("DesCell", forIndexPath: indexPath) as! UITableViewCell
if event?.description != "" {
// ...
}
else{
cell.hidden = true
}
return cell
default:
let cell = tableView.dequeueReusableCellWithIdentifier("PhotoCell", forIndexPath: indexPath) as! UITableViewCell
return cell
}
}
Until here no problem, it works properly!
Now, THE PROBLEM is that I would like to make the cells dynamics according to the cell contents (ex. description height). In order to do that, I have used
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 80.0
}
and if I comment the heightForRowAtIndexPath the cells are actually dynamics but I can't hide them anymore.
Do you have any suggestion on how to be able to hide the cells if they are empty and apply the automatic dimension according to their content?
lets say you have dynamic data and you want to show it in tableview so you need to create an array of your data to display.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet
var tableView: UITableView
var items: [String] = ["We", "Heart", "nothing" ,"" ,"imageurl", "", "xyz"]
override func viewDidLoad() {
super.viewDidLoad()
reloadTableAfterSorting()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func reloadTableAfterSorting(){
for var i = 0; i < self.items.count; i++
{
if self.items[i] == ""{
self.items.removeAtIndex(2)
}
}
self.tableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
}
For that i recommend you to sort the array before displaying it in the table view. Hiding the cell is not a good idea and its not good according to Apple recommendations. So you can do one thing except hiding the cell: remove the index from the array. In this way you can always have data to show in table and it will behave properly. So don’t try to hide the cell just pop the index from array.

Content of cells reordered when accessoryType is changed to Checkmark

I have an issue with TableViewCell height in iOS 7.1 when the checkmark is active in a row. When checkmark is on, the text of a cell is reordered and sometimes is put out of the bottom cell margin (see images), even if I resize the text label in Interface Builder.
Checkmark off:
Checkmark on:
This is my code for cells:
// MARK: Sections
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return arrayDomande.count
}
// MARK: Cells
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// 1 question & 5 answers
return 1 + 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier(QuestionCellIdentifier) as QuestionCellTableViewCell
configureCell(cell, forTableView: tableView, atIndexPath: indexPath)
cell.userInteractionEnabled = false
return cell
default:
let cell = tableView.dequeueReusableCellWithIdentifier(AnswerCellIdentifier) as AnswerCellTableViewCell
configureCell(cell, forTableView: tableView, atIndexPath: indexPath)
return cell
}
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
switch indexPath.row {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier(QuestionCellIdentifier) as QuestionCellTableViewCell
configureCell(cell, forTableView: tableView, atIndexPath: indexPath)
cell.layoutSubviews()
return (cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 10.0)
default:
let cell = tableView.dequeueReusableCellWithIdentifier(AnswerCellIdentifier) as AnswerCellTableViewCell
configureCell(cell, forTableView: tableView, atIndexPath: indexPath)
cell.layoutSubviews()
return (cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 10.0)
}
}
func configureCell(cell: UITableViewCell, forTableView: UITableView, atIndexPath: NSIndexPath) {
if (cell.isKindOfClass(QuestionCellTableViewCell)){
let domanda = arrayDomande[atIndexPath.section]
var attrs = [NSFontAttributeName: UIFont.boldSystemFontOfSize(19.0)]
var qString = NSMutableAttributedString(string: domanda.numero.stringValue + ". ", attributes:attrs)
var dString = NSMutableAttributedString(string: domanda.domanda)
qString.appendAttributedString(dString)
(cell as QuestionCellTableViewCell).testoLabel.attributedText = qString
} else if (cell.isKindOfClass(AnswerCellTableViewCell)) {
// Answers cache
// Add answers to arrayRisposte only if aren't already present
let domanda = arrayDomande[atIndexPath.section]
if arrayRisposte.indexForKey(atIndexPath.section) == nil {
let rispXDomanda: [Risposta] = domanda.risposte.allObjects as [Risposta]
arrayRisposte[atIndexPath.section] = rispXDomanda.shuffled()
}
let answers: [Risposta] = arrayRisposte[atIndexPath.section]!
(cell as AnswerCellTableViewCell).testoLabel.text = answers[atIndexPath.row - 1].risposta
(cell as AnswerCellTableViewCell).selectionStyle = UITableViewCellSelectionStyle.None
if (forTableView.indexPathsForSelectedRows() != nil) && (contains(forTableView.indexPathsForSelectedRows() as [NSIndexPath], atIndexPath)) {
(cell as AnswerCellTableViewCell).accessoryType = UITableViewCellAccessoryType.Checkmark
} else {
(cell as AnswerCellTableViewCell).accessoryType = UITableViewCellAccessoryType.None
}
}
}
// MARK: Select & Deselect
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
var inpSelectedRow = tableView.indexPathsForSelectedRows()
if inpSelectedRow != nil {
for selectedIndexPath in inpSelectedRow! {
if (selectedIndexPath.section == indexPath.section){
tableView.deselectRowAtIndexPath(selectedIndexPath as NSIndexPath, animated: false)
tableView.cellForRowAtIndexPath(selectedIndexPath as NSIndexPath)?.accessoryType = UITableViewCellAccessoryType.None
}
}
}
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = UITableViewCellAccessoryType.Checkmark
tableView.cellForRowAtIndexPath(indexPath)?.layoutSubviews()
return indexPath
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = UITableViewCellAccessoryType.None
}
There is a way, when checkmark is added, to change height of cells based on new cell heights or a way to make text label fixed?
Thank you guys!
Andrea
When checkmark is added. It will get added in accessory view. Accessory view takes some width which reduces the width of your label in the cell. So adjust your labels frame accordingly.

Resources