I have a tableView on mainStoryboard with two custom cells.
I would like to set two more cells at different row.
However When I implemented the code the added cells replaces original cells. (Custom cell of "Basic grammar3" and "Basic grammar5" are disappearing.)
I was trying to find the answer but could not find out.
I have image and code added below.
import UIKit
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tblStoryList: UITableView!
var array = PLIST.shared.mainArray
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.array.count + 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 || indexPath.row == 3 || indexPath.row == 5 {
let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as! HeaderCell
cell.headerTitle.text = indexPath.row == 0 ? "First Stage" : indexPath.row == 3 ? "Second Stage" : "Third Stage"
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "StoryTableviewCell", for: indexPath) as! StoryTableviewCell
//making plist file
let dict = self.array[indexPath.row - 1]
let title = dict["title"] as! String
let imageName = dict["image"] as! String
let temp = dict["phrases"] as! [String:Any]
let arr = temp["array"] as! [[String:Any]]
let detail = "progress \(arr.count)/\(arr.count)"
//property to plist file ăă€ăȘă
cell.imgIcon.image = UIImage.init(named: imageName)
cell.lblTitle.text = title
cell.lblSubtitle.text = detail
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
return
}
tableView.deselectRow(at: indexPath as IndexPath, animated:true)
if indexPath.row == 3 {
return
}
tableView.deselectRow(at: indexPath as IndexPath, animated:true)
if indexPath.row == 5 {
return
}
tableView.deselectRow(at: indexPath as IndexPath, animated:true)
let messagesVc = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
messagesVc.object = self.array[indexPath.row - 1]
self.navigationController?.show(messagesVc, sender: self)
}
You could use sections for your table view. Now, you are returning 1 in your numberOfSections function. And it is creating only one section. If you want to use headers, you can use sections for your need. And also you can fill your table view cells with multidimendional arrays. For example:
For adjusting your section headers:
let lessonTitles = ["First Stage", "Second Stage"]
Titles for sections:
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section < lessonTitles.count {
return lessonTitles [section]
}
return nil
}
For adjusting your sections and rows:
let lessons = [["Basic Grammar 1", "Basic Grammar 2"], ["Basic Grammar 3", "Basic Grammar 4"]]
Number of sections function should be:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return lessons.count
}
Number of rows in section should be:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lessons[section].count
}
And creating your cells is like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellText = data[indexPath.section][indexPath.row]
...
}
Try like this...
func numberOfSections(in tableView: UITableView) -> Int
{
return numberOfStages
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return numberOfRowsInCurrentStage
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
return customizedCell
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
{
return requiredHeight
}
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView?
{
return stageCountView
}
You can use viewForHeaderInSection if you want to show stage count on top.
edit: The comment by raki is the much better solution (use headers). I leave this here in case you want something closer to your existing implementation.
You have to change your numbering scheme in order to insert these additional rows (and not replace existing rows). So you might want to adjust the row for the "normal" elements like this:
func adjustRow(_ row: Int) -> Int {
if row < 3 {
return row
} else if row < 5 {
return row+1
} else {
return row+2
}
}
Related
I'm building an iOS app with a collection view inside the table view. I'm having three rows each row having a collection view inside it. I am planning to have three sections each section for each row. For example row, one should be in a separate section with a header and similarly for rows 2 and 3.
Whenever I create three sections I'm getting all the three rows in all three sections. I want to have a separate section with a header for each row.
import UIKit
class StoreVC: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var CourseTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
CourseTableView.tableFooterView = UIView()
CourseTableView.delegate = self
CourseTableView.dataSource = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0
{
return "Courses"
}
else if section == 1
{
return "Tests"
}
return "Bundles"
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CourseRow
return cell
}
else if indexPath.row == 1
{
let cell = tableView.dequeueReusableCell(withIdentifier: "testcell", for: indexPath) as! TestRow
return cell
}
else if indexPath.row == 2
{
let cell = tableView.dequeueReusableCell(withIdentifier: "bundlecell", for: indexPath) as! BundleRow
return cell
}
return UITableViewCell()
}
}
Try this code on your Xcode-playground and customize as you need.
import UIKit
import PlaygroundSupport
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
1
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UILabel()
headerView.text = "Header: \(section)"
return headerView
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "Cell: \(indexPath)"
return cell
}
}
PlaygroundPage.current.liveView = ViewController()
I have attached the image click the card view expands the same card inside the table cell dynamically its passible to achieve this?
I have searched a lot but not working
Hear my code added header cell with CardView
added arrow button to click the button expand the cell
its able expand but not in parent card it was showing diff card
I have adde my source code
var hiddenSections = Set<Int>()
let tableViewData = [
["1","2","3","4","5"],
["1","2","3","4","5"],
["1","2","3","4","5"],
]
override func viewDidLoad() {
super.viewDidLoad()
let CustomeHeaderNib = UINib(nibName: "CustomSectionHeader", bundle: Bundle.main)
historyTableView.register(CustomeHeaderNib, forHeaderFooterViewReuseIdentifier: "customSectionHeader")
}
func numberOfSections(in tableView: UITableView) -> Int {
return self.tableViewData.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.hiddenSections.contains(section) {
return 0
}
return self.tableViewData[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = self.tableViewData[indexPath.section][indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return view.frame.width/4
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = self.historyTableView.dequeueReusableHeaderFooterView(withIdentifier: "customSectionHeader") as! CustomSectionHeader
header.setupCornerRadious()
let sectionButton = header.expandBtn
sectionButton?.setTitle(String(section),
for: .normal)
sectionButton?.tag = section
sectionButton?.addTarget(self,action: #selector(self.hideSection(sender:)), for: .touchUpInside)
return header
}
#objc
private func hideSection(sender: UIButton) {
let section = sender.tag
func indexPathsForSection() -> [IndexPath] {
var indexPaths = [IndexPath]()
for row in 0..<self.tableViewData[section].count {
indexPaths.append(IndexPath(row: row,
section: section))
}
return indexPaths
}
if self.hiddenSections.contains(section) {
self.hiddenSections.remove(section)
self.historyTableView.insertRows(at: indexPathsForSection(),
with: .fade)
} else {
self.hiddenSections.insert(section)
self.historyTableView.deleteRows(at: indexPathsForSection(),
with: .fade)
}
}
With out sections also you can achieve this. To do this,
1.Return cell height as section height. If user clicks on the cell then return total content height to the particular cell.
2.You need to take an array, if user selects cell, add indexPath number in to array. If selects already expand cell remove it from array. In height for row at index check indexPath is in array or not.
This is one of the way. With sections also you can do that.
//MARK:- UITableView Related Methods
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrDict.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
// var cel = tblExpandedTest.dequeueReusableCellWithIdentifier("expCell", forIndexPath: indexPath) as! CDTableViewCell
var cel : CaseHearingTabTVC! = tableView.dequeueReusableCell(withIdentifier: "caseHearingTabCell") as! CaseHearingTabTVC
if(cel == nil)
{
cel = Bundle.main.loadNibNamed("caseHearingTabCell", owner: self, options: nil)?[0] as! CaseHearingTabTVC;
}
//cell?.backgroundColor = UIColor.white
cel.delegate = self
if indexPath != selctedIndexPath{
cel.subview_desc.isHidden = true
cel.subview_remarks.isHidden = true
cel.lblHearingTime.isHidden = true
}
else {
cel.subview_desc.isHidden = false
cel.subview_remarks.isHidden = false
cel.lblHearingTime.isHidden = false
}
return cel
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectIndex = true;
if(selectedInd == indexPath.row) {
selectedInd = -1
}
else{
let currentCell = tableView.cellForRow(at: indexPath)! as! CaseHearingTabTVC
cellUpdatedHeight = Float(currentCell.lblHearingTime.frame.origin.y + currentCell.lblHearingTime.frame.size.height) + 2;
selectedInd = -1
tblCaseHearing.reloadData()
selectedInd = indexPath.row
}
let previousPth = selctedIndexPath
if indexPath == selctedIndexPath{
selctedIndexPath = nil
}else{
selctedIndexPath = indexPath
}
var indexPaths : Array<IndexPath> = []
if let previous = previousPth{
indexPaths = [previous]
}
if let current = selctedIndexPath{
indexPaths = [current]
}
if indexPaths.count>0{
tblCaseHearing.reloadRows(at: indexPaths, with: UITableView.RowAnimation.automatic)
}
}
func tableView(_ tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowIndexPath indexPath:IndexPath) {
(cell as! CaseHearingTabTVC).watchFrameChanges()
}
func tableView(_ tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowIndexPath indexPath:IndexPath) {
(cell as! CaseHearingTabTVC).ignoreFrameChanges()
}
func tableView(_ TableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat{
if indexPath == selctedIndexPath{
return CGFloat(cellUpdatedHeight)
}else{
return CaseHearingTabTVC.defaultHeight
}
}
Best approach is to create two different cells for normal card and expanded card.
fileprivate var selectedIndex: Int?
func registerTableViewCells() {
tableView.register(UINib(nibName:Nib.CardCell , bundle: nil), forCellReuseIdentifier: "CardCell")
tableView.register(UINib(nibName:Nib.ExpandedCardCell , bundle: nil), forCellReuseIdentifier: "ExpandedCardCell")
}
override func viewDidLoad() {
super.viewDidLoad()
self.registerTableViewCells()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
guard let index = selectedIndex else {
return 115
}
if index == indexPath.row{
return 200
}
return 115
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let selected = selectedIndex, selected == indexPath.row{
let cell = tableView.dequeueReusableCell(withIdentifier: "ExpandedCardCell", for: indexPath) as! ExpandedCardCell
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "CardCell", for: indexPath) as! CardCell
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedIndex == indexPath.row{
selectedIndex = nil
}
else{
selectedIndex = indexPath.row
}
UIView.performWithoutAnimation {
tableView.reloadData()
}
}
class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
var questions:[Question] = []
var sectionCountGlobal = 0
override func viewDidLoad() {
super.viewDidLoad()
questions = fillQuestions()
}
func fillQuestions()-> [Question]{
var temp : [Question] = []
var choices : [Choice] = []
let choice = Choice(id: 1, text: "choice ", status: 1, questionId: 1)
choices.append(choice)
choices.append(choice)
choices.append(choice)
choices.append(choice)
choices.append(choice)
choices.append(choice)
let q1 = Question(id: 1, text: "Ahmad 55 years old man with a history of hypertension and hypocholesteremia was in a wedding and during the party he starts to feel chest pain and dizzy, his wife brought him to the emergency department. The ER nurse checked his vital signs: BP 88/50, HR: 45, RR:10, SPaO2: 90% and O2 per nasal cannula was started at 4l/minute. Few seconds later Mr.Ahmad lost consciousness and the code blue team were activated.", choices: choices)
let q2 = Question(id: 1, text: "question 2", choices: choices)
let q3 = Question(id: 1, text: "question 3", choices: choices)
temp.append(q1)
temp.append(q2)
temp.append(q3)
return temp
}
func numberOfSections(in tableView: UITableView) -> Int {
return questions.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
sectionCountGlobal = section
return questions[section].choices.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let questionTextCell = tableView.dequeueReusableCell(withIdentifier: "QuestionTextCell") as! QuestionTextCell
questionTextCell.setQuestionText(text: questions[indexPath.section].text)
return questionTextCell
}else{
let choiceCell = tableView.dequeueReusableCell(withIdentifier: "ChoiceCell") as! ChoiceCell
choiceCell.choiceText.text = questions[indexPath.section].choices[indexPath.row].text
return choiceCell
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let questionNumber = "Q" + String(section+1)
return questionNumber
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 3
}
override func viewWillAppear(_ animated: Bool) {
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
}
}
I am working on a quiz app and there is multiple choices for each question so when checking the radio button in a cell and scroll to other cells i found that the other cells got checked without touching them what is the solution.
I tried different cell reusing methods also prepareForReuse() and nothing works how can i treat each cell independently without affect from other cells , i don't know the number of questions it is come from server.
In your cellForRowAt implementation you have to reset the cell's state according to whether it is selected or not. Due to cell reuse, you can get a cell which was previously selected, but now should not be selected - in that case you have to tell the cell to get unselected (and vice versa):
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let questionTextCell = tableView.dequeueReusableCell(withIdentifier: "QuestionTextCell") as! QuestionTextCell
questionTextCell.setQuestionText(text: questions[indexPath.section].text)
return questionTextCell
} else {
let choiceCell = tableView.dequeueReusableCell(withIdentifier: "ChoiceCell") as! ChoiceCell
// here detect if the cell should be selected and set it accordingly, so something like:
let isSelected = isSelectedChoice(questions[indexPath.section].choices[indexPath.row])
choiceCell.isSelected = isSelected
// of course this is just a mockup, since I don't know exactly how you manage selection,
// but it should get you on the right path
choiceCell.choiceText.text = questions[indexPath.section].choices[indexPath.row].text
return choiceCell
}
}
The issue in you code is you not changing the status of your radio button. When you select the option from didSelectRowAt method, you have to change the status of your choice. As per your choice model you can change the status of particular choice status. Following are both method that can manage your selection of choice(your status variable should be Bool type):
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let questionTextCell = tableView.dequeueReusableCell(withIdentifier: "QuestionTextCell") as! QuestionTextCell
questionTextCell.setQuestionText(text: questions[indexPath.section].text)
return questionTextCell
}else{
let choiceCell = tableView.dequeueReusableCell(withIdentifier: "ChoiceCell") as! ChoiceCell
// update your radio button UI
choiceCell.radioButton.isSelected = questions[indexPath.section].choices[indexPath.row].status
choiceCell.choiceText.text = questions[indexPath.section].choices[indexPath.row].text
return choiceCell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
questions[indexPath.section].choices[indexPath.row].status = !questions[indexPath.section].choices[indexPath.row].status
tableView.reloadData()
}
I have a tableview with different sections, I need to be able to multiselect from different sections, but the rows in each section should be able to select mutually exclusive wise. For eg: in below screenshot I should be able to select either Margarita or BBQ Chicken from Pizza and same for Deep dish pizza but I should be able to multiselect between Pizza section and Deep dish pizza
Below is my code so far, I was wondering what would be the best way to approach this.
let section = ["Pizza", "Deep dish pizza"]
let items = [["Margarita", "BBQ Chicken"], ["Sausage", "meat lovers"]]
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.section[section]
}
override func numberOfSections(in tableView: UITableView) -> Int {
return section.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items[section].count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)
// Configure the cell...
cell.textLabel?.text = items[indexPath.section][indexPath.row]
return cell
}
You should create some data element to track the selection for each row.
I would suggest a dictionary of [Int:Int] where the key is the section and the value is the row.
When a row is selected, you can then easily check to see if another row is already selected in that section, and deselect it if required.
var rowSelections = [Int:Int]()
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let section = indexPath.section
if let row = self.rowSelections[section] {
tableView.deselectRow(at: IndexPath(row:row, section:section), animated: true)
}
self.rowSelections[section]=indexPath.row
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let section = indexPath.section
self.rowSelections[section]=nil
}
Ok, so I figured it out, I created a method that would loop and check through all the rows and called it in both tableview didselect and deselect
func updateTableViewSelections(selectedIndex:IndexPath)
{
for i in 0 ..< tableView.numberOfSections
{
for k in 0 ..< tableView.numberOfRows(inSection: i)
{
if let cell = tableView.cellForRow(at: IndexPath(row: k, section: i))
{
if sections.getType(index: i) == selectedIndex.section
{
if (selectedIndex.row == k && cell.isSelected)
{
cell.setSelected(cell.isSelected, animated: false)
}
else
{
cell.setSelected(false, animated: false)
}
}
}
}
}
}
First allow multiple selection:
yourTableView.allowsMultipleSelection = true
To get the select rows:
let selectedRows = tableView.indexPathsForSelectedRows
Then within the didselectrow function you can iterate through the selected rows and ensure that only 1 row within the section can be selected.
I am loading data into a UITableView. The first load happens properly for the first 10 cells in
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}
the indexPath.row increments properly and loads the data into the proper cells from the data source. I then implemented a load more when the bottom of the table is reached. Now func tableView is called but it is stuck at indexPath.row = 9. I have implemented a checker in
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
And it appears that the proper number of rows has been added.
Edit: I having issue with the my second uitableview (there are two in this scene) The checker is a print statement that is called and returns the proper uitableView and this happens before the tableView gets stuck at the same value.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.table {
return users2.count
}
else {
print("married barry", tableFeedCount)
return tableFeedCount
}
}
Try following:
Declare boolean
let boolNotMoreData : Bool = true
Append new data to your data source
let arrResponse: [Any]? = (responseObject["news"] as? [Any])
if arrResponse?.count == 0{
boolNotMoreData = false;
}
for dictResponse in arrResponse as! [[String: Any]] {
self.arrDataSource.append(NewsClass(responseDict: dictResponse))
}
self.tblViewNews?.reloadData()
Now fetch new data
private func tableView(_ tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == arrNews.count - 1 {
if boolNotMoreData {
currentPage += 1
getYourData()
}
}
}
This worked Successfully
#IBOutlet weak var Submitted: UITableView!
#IBOutlet weak var ViewAssigenment: UITableView!
var Arrayone:[String] = []
var ArrayTwo:[String] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
var count:Int?
if tableView == self.ViewAssigenment
{
count = Arrayone.count
}
else if tableView == self.Submitted
{
count = ArrayTwo.count
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if tableView == self.ViewAssigenment
{
let cell = tableView.dequeueReusableCell(withIdentifier: "ViewCell") as!
ViewAssigenmentTableViewCell
let obj =Arrayone[indexPath.row]
cell.lblTitle.text = obj.AssTitle
return cell
}
else
{
let cell1 = tableView.dequeueReusableCell(withIdentifier: "Submittcell") as! SubmittedAssigenmentTableViewCell
let obj2 = ArrayTwo[indexPath.row]
cell1.lbltitle.text = obj2.AssTitle
return cell1
}
}