How to show a parent view which is outside tableview and is scrollable? - ios

I have a scenario where I need to show a parent view with shadow and corner radius containing a long list of reusable items. I used a tableView to display items. But I stuck at making my tableview expand as much as its contentSize. It works but not accurate. Any solutions?
Edit:
Desired result:
I used the following reference for self sizing tableview.
Self Sizing UITableView
I made a few modifications as below:
final class SelfSizedTableView: UITableView {
var maxHeight = CGFloat.greatestFiniteMagnitude
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
self.layoutIfNeeded()
}
override var intrinsicContentSize: CGSize {
let height = min(contentSize.height, maxHeight)
let size = CGSize(width: contentSize.width, height: height)
return size
}
}
I used a parent tableView with a cell having my containerView and embedding this self sized tableView.
class MyContainerViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// MARK: - IBOutlets
#IBOutlet weak var parentTableView: UITableView!
// MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func estimateDataHeight() -> CGFloat {
let detailCellHeight: CGFloat = 32
let headingCellHeight: CGFloat = 43
let headings: CGFloat = headingCellHeight*2
let detailsHeight: CGFloat = detailCellHeight*4
let baseHeight = headings + detailsHeight
let membersHeight =
CGFloat(sectionsArray.count) * detailCellHeight
return baseHeight + membersHeight
}
}
// MARK: - UITableViewDataSource
extension MyContainerViewController {
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let id = String(describing: MyContainerTVCell.self)
guard let cell = tableView
.dequeueReusableCell(withIdentifier: id, for: indexPath)
as? MyContainerTVCell else {
return UITableViewCell()
}
cell.policyDetails = dataSource
// my cheat/trick doesn't work on large data.
DispatchQueue.main.asyncAfter(deadline: .now()+0.4) {
tableView.beginUpdates()
cell.tableView.layoutIfNeeded()
cell.tableView.reloadData() // the overridden one
tableView.endUpdates()
}
return cell
}
}
extension MyContainerViewController {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return estimateDataHeight()
}
}
My cell class which has the self size tableView and containerView:
class MyContainerTVCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {
// MARK: - IBOutlets
#IBOutlet weak var containerView: UIView!
#IBOutlet weak var shadowView: UIView!
#IBOutlet weak var tableView: SelfSizedTableView!
// MARK: - Properties
let titles = ["Email ID:", "Mobile Number:", "Address:", "ID: "] // first section data array
let moreData: [String] = [] // remaining reusable sections array
// no of subsequent sections for moreData array type
var numberOfSections: Int {
return 4
}
// MARK: -
var dataSource: MyDataSource!
// MARK: - Life Cycle
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
override func layoutSubviews() {
super.layoutSubviews()
}
// MARK: - Setup
func setupView() {
containerView.rounded(with: 10)
shadowView.layer.applyShadow()
tableView.dataSource = self
tableView.delegate = self
}
}
// MARK: - UITableViewDataSource
extension MyContainerTVCell {
func numberOfSections(in tableView: UITableView) -> Int {
return numberOfSections + 1
}
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
if section == 0 { return titles.count + 1 }
else if section == 1 { return moreData.count + 1 }
else { return moreData.count }
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let headerID = String(describing: MyHeaderTVCell.self)
let itemID = String(describing: MyItemTVCell.self)
switch indexPath.section {
case 0:
if indexPath.row == 0 {
guard let cell = tableView
.dequeueReusableCell(withIdentifier: headerID, for: indexPath)
as? MyHeaderTVCell else {
return UITableViewCell()
}
cell.titleLabel.text = dataSource.title
return cell
} else {
guard let cell = tableView
.dequeueReusableCell(withIdentifier: itemID, for: indexPath)
as? MyItemTVCell else {
return UITableViewCell()
}
let item = titles[indexPath.row-1]
cell.titleLabel.text = item
cell.separatorView.isHidden = true
let data: String
switch indexPath.row {
case 1:
data = dataSource.emailID
case 2:
data = dataSource.mobileNo
case 3:
data = dataSource.address
case 4:
data = dataSource.name
case 5:
data = dataSource.age
case 6:
data = dataSource.id
case 7:
data = dataSource.office
case 8:
data = dataSource.academic
default: data = String()
}
cell.detailLabel.text = data
return cell
}
case 1:
if indexPath.row == 0 {
guard let cell = tableView
.dequeueReusableCell(withIdentifier: headerID, for: indexPath)
as? MyHeaderTVCell else {
return UITableViewCell()
}
cell.titleLabel.text = "More Data"
return cell
} else {
guard let cell = tableView
.dequeueReusableCell(withIdentifier: itemID, for: indexPath)
as? MyItemTVCell else {
return UITableViewCell()
}
let sectionIndex = indexPath.section-1
guard sectionIndex <= numberOfSections-1,
let section = sectionsArray?[indexPath.section-1] else {
return UITableViewCell()
}
cell.titleLabel.text = moreData[indexPath.row-1]
cell.separatorView.isHidden = true
switch indexPath.row {
case 1:
cell.detailLabel.text = section.a
case 2:
cell.detailLabel.text = section.b
case 3:
cell.detailLabel.text = "\(section.c ?? 0)"
case 4:
cell.detailLabel.text = section.d
case 5:
cell.detailLabel.text = section.e
case 6:
cell.detailLabel.text = section.f
if indexPath.section < numberOfSections {
cell.separatorView.isHidden = false
}
default: break
}
return cell
}
default:
guard let cell = tableView
.dequeueReusableCell(withIdentifier: itemID, for: indexPath)
as? MyItemTVCell else {
return UITableViewCell()
}
let sectionIndex = indexPath.section-1
guard sectionIndex <= numberOfSections-1,
let section = sectionsArray?[indexPath.section-1] else {
return UITableViewCell()
}
cell.titleLabel.text = moreData[indexPath.row]
cell.separatorView.isHidden = true
switch indexPath.row {
case 0:
cell.detailLabel.text = section.a
case 1:
cell.detailLabel.text = section.b
case 2:
cell.detailLabel.text = "\(section.c ?? 0)"
case 3:
cell.detailLabel.text = section.d
case 4:
cell.detailLabel.text = section.e
case 5:
cell.detailLabel.text = section.f
if indexPath.section < numberOfSections {
cell.separatorView.isHidden = false
}
default: break
}
return cell
}
}
}
// MARK: - UITableViewDelegate
extension MyContainerTVCell {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 && indexPath.row == 0 { return 43 }
if indexPath.section == 1 && indexPath.row == 0 { return 43 }
return 32
}
}

Why would you want to expand tableView as much as its content size to make it scrollable, when tableView is already scrollable?
However, if you have some other content, aside from table, on the screen and you want them to scroll together, then you need to embed all your content into UIScrollView.
Then, make a height constraint for you tableView in xib/storyboard with any value.
Then you might do something like this:
// in your view controller
private var heightObservation: NSKeyValueObservation?
// called once, for example, in viewDidLoad()
private func setupTableView() {
...
observation = tableView.constraintFrameHeightToContentSizeHeight()
}
extension UITableView {
func constraintFrameHeightToContentSizeHeight() -> NSKeyValueObservation {
return observe(\.contentSize, changeHandler: { (tableView, _) in
tableView.heightConstraint?.constant = tableView.contentSize.height
})
}
}
// find height constraint
extension UIView {
var heightConstraint: NSLayoutConstraint? {
return constraints.first(where: { $0.firstAttribute == .height })
}
}
Don't forget to uncheck "Scrolling Enabled" in xib/storyboard for that table view.

Related

TableView cells becomes inactive

I am using a tableView to take some surveys.
Header I use for a question. Footer for «back» and «next» buttons. And tableView cells for answer options.
Now I started to have a problem, with some user interaction: when you simultaneously click on the “next” button and select an answer, the answer options cease to be active, nothing can be selected. Although the buttons remain active.
Tell me in what direction to look for the problem and how you can debug this problem in order to understand what's wrong.
It all started after fixing bugs, when the application crashed when simultaneously (or almost) pressing the "next" button and choosing an answer. Because the didSelectRowAt method worked after I changed the current array of answer options, and the selected index in the previous question turned out to be larger than the size of the array with the answers to the new question.
class AssessmentVC: UIViewController {
#IBOutlet weak var tableView: UITableView!
var footer: FooterTableView?
var header: UIView?
var arrayAssessmnet = [AssessmentDM]()
var assessment: AssessmentDM!
var question: QuestionDM!
var viewSeperationHeader = UIView()
var arrayOptions: [Option]?
var countAssessment = 0
var numberAssessment = 0
var numberQuestion = 0
var countQuestion = 0
var numberQusttionForLabel = 1
var arrayQuestion = [QuestionDM]()
var arrayAnswers = [AnswerDM]()
var arrayEvents = [EventDM]()
override func viewDidLoad() {
super.viewDidLoad()
settingAssessment()
}
//MARK: - settingAssessment()
private func settingAssessment() {
let id = self.assessment.serverId
arrayQuestion = QuestionDM.getQuestions(id: id)
assessmentName.text = assessment.name
countQuestion = arrayQuestion.count
let day = self.assessment.day
arrayAnswers = AnswerDM.getAnswers(idAssessment: id, day: day)
settingQuestion(eventType: .start)
}
//MARK: - settingQuestion()
private func settingQuestion(eventType: EventType? = nil) {
let prevQuestion = question
question = arrayQuestion[numberQuestion]
timeQuestion = 0
footer!.grayNextButton()
//first question
if numberQuestion == 0 && numberAssessment == 0 {
footer!.previousButton.isHidden = true
} else {
footer!.previousButton.isHidden = false
}
arrayOptions = [Option]()
let sortOption = question.options!.sorted {$0.numberOption < $1.numberOption}
for option in sortOption {
arrayOptions?.append(Option(label: option.label, value: option.value))
}
tableView.rowHeight = UITableView.automaticDimension
tableView.reloadData()
heightTableView()
tableView.setContentOffset(.zero, animated: false)
}
//MARK: - heightTableView()
func heightTableView() {
}
//MARK: - UITableViewDataSource
extension AssessmentVC: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
viewSeperationHeader.isHidden = false
footer?.viewSeperationFooter.isHidden = false
tableView.separatorStyle = .singleLine
return question.options?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(forIndexPath: indexPath as IndexPath) as AnswerAssessmentCell
cell.initCell(text: arrayOptions![indexPath.row].label, value: arrayOptions![indexPath.row].value, arrayValue: arrayAnswers[numberQuestion].response, isCheckbox: true)
return cell
}
}
//MARK: - UITableViewDelegate
extension AssessmentVC: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
isChangAnswerInAssessment = true
if question.answerType == "Radio" || question.answerType == "Checkbox"{
selectRadioOrChekbox(indexPath: indexPath)
}
}
}
//MARK: - selectRadioOrChekbox
extension AssessmentVC {
private func selectRadioOrChekbox(indexPath: IndexPath) {
if question.answerType == "Radio" {
let cells = tableView.visibleCells as! Array<AnswerAssessmentCell>
for cell in cells {
cell.select = false
cell.isSelected = false
}
let cell = tableView.cellForRow(at: indexPath) as! AnswerAssessmentCell
cell.select = true
cell.isSelected = true
if arrayOptions?.count ?? 0 > indexPath.row {
arrayAnswers[numberQuestion].response = arrayOptions![indexPath.row].value
footer?.greenNextButton()
}
}
if question.answerType == "Checkbox" {
if arrayOptions?.count ?? 0 > indexPath.row {
//если нажато что-то, что должно сбросить "None"
// question.options![0].isSelect = false
let cells = tableView.visibleCells as! Array<AnswerAssessmentCell>
if cells[0].answerLabel.text == "None" {
cells[0].select = false
cells[0].isSelected = false
}
var array = arrayAnswers[numberQuestion].response?.components(separatedBy: ";")
array?.removeAll { $0 == "0"}
if array?.count == 0 {
arrayAnswers[numberQuestion].response = nil
} else {
arrayAnswers[numberQuestion].response = array?.joined(separator: ";")
}
let cell = tableView.cellForRow(at: indexPath) as! AnswerAssessmentCell
cell.select = !cell.select
cell.isSelected = cell.select
arrayAnswers[numberQuestion].response = array.joined(separator: ";")
if array.count == 0 {
arrayAnswers[numberQuestion].response = nil
footer?.grayNextButton()
} else {
footer?.greenNextButton()
}
}
}
}
}
//MARK: - Navigation between questions
extension AssessmentVC {
func nextQuestion() {
footer!.grayNextButton()
numberQuestion += 1
numberQusttionForLabel += 1
settingQuestion(eventType: .next)
} else {
}
func previousQuestion() {
numberQusttionForLabel -= 1
settingQuestion(eventType: .previous)
}
}
Some snippets that can help you :
// Answer type : use enum . Here the Strong/Codable is if you want to
// save using JSON encoding/decoding
enum AnswerType: String, Codable {
case checkBox = "CheckBox"
case radio = "Radio"
}
Setup of your cell :
class AnswerAssessmentCell: UITableViewCell {
...
// work with Option type
func initCell(option: Option, response: String?, answerType: AnswerType) {
// setup cell contents (labels)
// check for selected status
switch answerType {
case .checkBox:
// check if option is in response
// set isSelected according
break
case .radio:
// check if option is response
// set isSelected according
break
}
}
}
In table view data source :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! AnswerAssessmentCell
// Use the option to init the cell
// this will also set the selected state
let optionNumber = indexPath.row
cell.initCell(option: arrayOptions![optionNumber], response: arrayAnswers[numberQuestion].response, answerType: question.answerType)
return cell
}
In Table view delegate :
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
isChangAnswerInAssessment = true
let optionNumber = indexPath.row
switch question.answerType {
case .radio:
selectRadio(optionNumber: optionNumber)
case .checkBox:
selectCheckBox(optionNumber: optionNumber)
}
// Reload tableview to show changes
tableView.reloadData()
}
// Separate in 2 function for smaller functions
// in this function work only with model data, the reload data will do
// cell update
// only the footer view button cooler may need to be changed
private func selectRadio(optionNumber: Int) {
// Reset current response
// set response to optionNumber
// update footer button cooler if necessary
}
private func selectCheckBox(optionNumber: Int) {
// if option is in response
// remove option from response
// else
// add response to option
// update footer button cooler if necessary
}
Hope this can help you

Swift 5 - make tableview wait until data from api call comes back (using multiple tableviews)

Issue: Fatal error when View is loading. I know the problem is because there is no data available when to table view is trying to load. But, because I am using multiple TableViews in one UI I have to force the cell return in CellForRowAt.
Is there a better way of setting up different data for each TableView?
THANKS FOR THE HELP!
import UIKit
class NewCustomTaskVC: UIViewController {
#IBAction func CancelPressed (_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBOutlet weak var taskTypeSelectionBtn: UIButton!
#IBOutlet weak var FrameSelectionBtn: UIButton!
#IBOutlet weak var AssignSelectionBtn: UIButton!
#IBAction func SelecttaskTypePressed(_ sender: Any) {
if tableViewTaskType.isHidden {
self.tableViewTaskType.isHidden = false
self.tableViewTaskType.rowHeight = 43.5
} else {
self.tableViewTaskType.isHidden = true
}
}
#IBAction func SelectFramePressed(_ sender: Any) {
if tableViewFrame.isHidden {
self.tableViewFrame.isHidden = false
} else {
self.tableViewFrame.isHidden = true
}
}
#IBAction func SelectAssignToPressed(_ sender: Any) {
if tableViewAssignTo.isHidden {
self.tableViewAssignTo.isHidden = false
} else {
self.tableViewAssignTo.isHidden = true
}
}
#IBOutlet weak var tableViewTaskType: UITableView!
#IBOutlet weak var tableViewFrame: UITableView!
#IBOutlet weak var tableViewAssignTo: UITableView!
var cellID = ""
var array = ["String", "Test", "Next","Test 2", "Test 3"]
override func viewDidLoad() {
super.viewDidLoad()
getData()
tableViewTaskType.isHidden = true
tableViewFrame.isHidden = true
tableViewAssignTo.isHidden = true
tableViewTaskType.delegate = self
tableViewFrame.delegate = self
tableViewAssignTo.delegate = self
tableViewTaskType.dataSource = self
tableViewFrame.dataSource = self
tableViewAssignTo.dataSource = self
self.tableViewTaskType.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
self.tableViewFrame.register(UITableViewCell.self, forCellReuseIdentifier: "cell2")
self.tableViewAssignTo.register(UITableViewCell.self, forCellReuseIdentifier: "cell3")
}
func getData () {
//dispatchGroup.enter()
var count = 0
APICallBack.getFramesData(completion: { success in
if success == true {
print("frames success")
count += 1
} })
APICallBack.getTaskTypeData { success in
if success == true {
print("task success")
count += 1
}
}
APICallBack.GETUserData(completion: { success in
if success == true {
print("user success")
count += 1
} })
if count == 3{
DispatchQueue.main.async {
self.tableViewTaskType.reloadData()
self.tableViewAssignTo.reloadData()
self.tableViewFrame.reloadData()
print("ALL COMPLETE")
}
}
}
}
extension NewCustomTaskVC : UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count = 1
switch tableView {
case tableViewTaskType:
count = TaskTypeData.typeModel.count
case tableViewFrame:
count = FramesData.framesModel.count
case tableViewAssignTo:
count = CustomerData.customerModel.count
default:
print("none")
return count
}
//return 5
return count
}
**PROBLEM IS HERE
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView == self.tableViewTaskType{
cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
cell!.textLabel!.text = TaskTypeData.typeModel[indexPath.row].TaskTypeName
// cell!.textLabel?.text = array[indexPath.row]
}
if tableView == tableViewFrame{
cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
cell!.textLabel!.text = FramesData.framesModel[indexPath.row].FrameName
// cell!.textLabel?.text = array[indexPath.row]
}
if tableView == self.tableViewAssignTo {
cell = tableView.dequeueReusableCell(withIdentifier: "cell3", for: indexPath)
cell!.textLabel!.text = UserData.userModel[indexPath.row].UserFirst
// cell.textLabel?.text = array[indexPath.row]
}
// let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
// cell.textLabel?.text = array[indexPath.row]
return cell!
}
** TO HERE!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = array[indexPath.row]
print(item)
tableViewTaskType.isHidden = true
}
}
My UI View:
The error I get if I don't force it:
I assume that you want to display an empty "placeholder" cell if the data source array is empty. You need to check explicitly for that condition in your cellForRow function.
As far as co-ordinating the fetch from multiple API endpoints, you can use a DispatchGroup - Some commented code indicates you may have tried this.
override func viewDidLoad() {
super.viewDidLoad()
tableViewTaskType.isHidden = true
tableViewFrame.isHidden = true
tableViewAssignTo.isHidden = true
tableViewTaskType.delegate = self
tableViewFrame.delegate = self
tableViewAssignTo.delegate = self
tableViewTaskType.dataSource = self
tableViewFrame.dataSource = self
tableViewAssignTo.dataSource = self
self.tableViewTaskType.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
self.tableViewFrame.register(UITableViewCell.self, forCellReuseIdentifier: "cell2")
self.tableViewAssignTo.register(UITableViewCell.self, forCellReuseIdentifier: "cell3")
getData()
}
func getData () {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
APICallBack.getFramesData(completion: { success in
if success == true {
print("frames success")
}
dispatchGroup.leave()
})
APICallBack.getTaskTypeData { success in
if success == true {
print("task success")
}
dispatchGroup.leave()
}
APICallBack.GETUserData(completion: { success in
if success == true {
print("user success")
}
dispatchGroup.leave()
})
dispatchGroup.notify {
self.tableViewTaskType.reloadData()
self.tableViewAssignTo.reloadData()
self.tableViewFrame.reloadData()
print("ALL COMPLETE")
}
}
extension NewCustomTaskVC : UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch tableView {
case tableViewTaskType:
return max(1,TaskTypeData.typeModel.count)
case tableViewFrame:
return max(1,FramesData.framesModel.count)
case tableViewAssignTo:
return max(1,CustomerData.customerModel.count)
default:
fatalError("Unexpected table view")
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch tableView {
case self.tableViewTaskType:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
if !TaskTypeData.typeModel.isEmpty {
cell.textLabel!.text = TaskTypeData.typeModel[indexPath.row].TaskTypeName
}
return cell
case tableViewFrame:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
if !FramesData.framesModel.isEmpty {
cell!.textLabel!.text = FramesData.framesModel[indexPath.row].FrameName
}
return cell
case self.tableViewAssignTo:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell3", for: indexPath)
if !UserData.userModel.isEmpty {
cell!.textLabel!.text = UserData.userModel[indexPath.row].UserFirst
}
return cell
default:
fatalError("Unexpected Tableview")
}
}
you can set tableView.dataSource & tableView.delegate to self when your data is back
There is multiple problems with your code.
1) You call get data before your table view had registered it cells.
So if your API would load data immediately table view will be call dataSource methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
but since the
self.tableViewTaskType.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
self.tableViewFrame.register(UITableViewCell.self, forCellReuseIdentifier: "cell2")
self.tableViewAssignTo.register(UITableViewCell.self, forCellReuseIdentifier: "cell3")
called in the end of viewDidLoad, your would get crash when you dequeue your cells in cellForAtIndexPath method.
The solution is to move getData call to the end of viewDidLoad method.
2) If you want to display all tables data at one time (when API is complete loading getFramesData, getTaskTypeData and GETUserData) you would need to synchronise this callbacks. You could do this with DispatchGroup.
func getData () {
let apiDispatchGroup = DispatchGroup()
APICallBack.getFramesData { success in
apiDispatchGroup.leave()
}
apiDispatchGroup.enter()
APICallBack.getTaskTypeData { success in
apiDispatchGroup.leave()
}
apiDispatchGroup.enter()
APICallBack.GETUserData { success in
apiDispatchGroup.leave()
}
apiDispatchGroup.enter()
apiDispatchGroup.notify(queue: DispatchQueue.main) {
self.tableViewTaskType.reloadData()
self.tableViewAssignTo.reloadData()
self.tableViewFrame.reloadData()
}
}
3) It's not good idea to use one dataSOurce class for mutltiple UITableView, because of dataSource become god object. The better approach is to use one ContainerViewController that contains three child UITableViewController, and pass data to the childs when data has loaded from the API.
It is perfectly fine to have multiple view controllers in one screen. So I suggest that you create three view controllers, one for each table view. And each table view has its own datasource. Then use a custom container view controller as described here: https://developer.apple.com/documentation/uikit/view_controllers/creating_a_custom_container_view_controller

How to increment and decrement value of label in tableview and make total price from label value in swift?

now cell value are dynamically and its look after calling api.
I want to make total of all tickets price at last. I refer this link How do I increment/decrement a label value with two buttons pressed in tableview Swift and make changes in my code but didn't work for me.
struct Product {
var price = 0
}
class TicketBookingVC: UIViewController , UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tblView: UITableView!
#IBOutlet weak var mainTblView: UIView!
var bookingDetails = NSDictionary()
var productArray = [Product]()
var product : Product!
private var counterValue = 1
var productIndex = 0
var counterLbl = UILabel()
#IBOutlet weak var bookBtn: UIButton!
#IBOutlet weak var eventImg: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
tblView.delegate = self
tblView.dataSource = self
for _ in 0...10{
productArray.append(Product(price: 1))
}
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
}
else if section == 1{
return 4
}
else{
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellfirst", for: indexPath)
cell.selectionStyle = .none
return cell
}
else if indexPath.section == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellsecond", for: indexPath)
let mainViewCell = cell.contentView.viewWithTag(2000) as! UIView
let normalView = cell.contentView.viewWithTag(2001) as! UIView
let eventName = cell.contentView.viewWithTag(2003) as! UILabel
let eventPrice = cell.contentView.viewWithTag(2004) as! UILabel
counterLbl = cell.contentView.viewWithTag(2007) as! UILabel
let decrementBtn = cell.contentView.viewWithTag(2005) as! UIButton
let incrementBtn = cell.contentView.viewWithTag(2006) as! UIButton
decrementBtn.addTarget(self, action:#selector(self.decrementbuttonClicked), for: .touchUpInside)
incrementBtn.addTarget(self, action:#selector(self.incrementbuttonClicked), for: .touchUpInside)
product = productArray[indexPath.row]
counterLbl.text = "\(product.price)"
cell.selectionStyle = .none
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellthird", for: indexPath)
cell.selectionStyle = .none
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0{
return UITableView.automaticDimension
}
else{
return 80
//return UITableView.automaticDimension
}
}
#objc func decrementbuttonClicked() {
print("Button decrement")
if(counterValue != 1){
counterValue -= 1;
}
self.counterLbl.text = "\(counterValue)"
product.price = counterValue
}
#objc func incrementbuttonClicked() {
counterValue += 1;
self.counterLbl.text = "\(counterValue)"
product.price = counterValue
}
func addProductToCart(product: Product, atindex: Int) {
productArray[atindex] = product
calculateTotal()
}
func calculateTotal()
{
var totalValue = 0
for objProduct in productArray {
totalValue += objProduct.price
}
self.eventPrice.text = "Total \(totalValue)"
}
}
when I increment or decrement value of first cell it reflect in 4th cell. please help. I am new at swift.
This is due to cell reuse. You should set a model for each cell

How to navigate to viewController from tableview cell, which is placed in other tableview Cell?

I have placed a tableview2 in tableview1 Cell, Now when I click on the tableview2 cell I need to navigate to a new viewController. Please help me... I was struggling with one whole day :(
here is the code, the second table view is placed in SegmentedCell...
when i am trying to push, its unable to go next controller..
import UIKit
import XMSegmentedControl
import Alamofire
import SwiftyJSON
class segmentedCell: UITableViewCell, XMSegmentedControlDelegate, UITableViewDelegate, UITableViewDataSource{
let byndrColor : UIColor = UIColor( red: 224/255, green: 0/255, blue: 115/255, alpha: 1.0 )
let fontStyle = UIFont(name: "Lato-bold", size: 12)
#IBOutlet weak var segmentedControl: XMSegmentedControl!
#IBOutlet weak var feedTableView: UITableView!
var getApi = UIApplication.shared.delegate as! AppDelegate
var course_id = String()
var materialListObjects = [MaterialsInSingleCourseGetSet]()
var assignmentExamAndQuizListObjects = [AssignmentAndExamsQuizGetSet]()
override func awakeFromNib() {
super.awakeFromNib()
feedTableView.delegate = self
feedTableView.dataSource = self
segmentedControl.delegate = self
segmentedControl.segmentTitle = ["LATEST", "MATERIALS", "COURSEWORK", "PROGRESS"]
segmentedControl.font = fontStyle!
segmentedControl.selectedItemHighlightStyle = XMSelectedItemHighlightStyle.BottomEdge
segmentedControl.backgroundColor = UIColor.white
segmentedControl.tint = UIColor.black
segmentedControl.highlightTint = byndrColor
segmentedControl.highlightColor = byndrColor
segmentedControl.edgeHighlightHeight = 2
segmentedControl.selectedSegment = 0
let share = UIApplication.shared.delegate as! AppDelegate
materialListObjects = share.materialListInSingleCourse as! [MaterialsInSingleCourseGetSet]
assignmentExamAndQuizListObjects = share.assignmentsExamsAndQuizListInSingleCourse as! [AssignmentAndExamsQuizGetSet]
// Initialization code
}
func xmSegmentedControl(xmSegmentedControl: XMSegmentedControl, selectedSegment: Int) {
if xmSegmentedControl == segmentedControl {
print("SegmentedControl1 Selected Segment: \(selectedSegment)")
switch segmentedControl.selectedSegment
{
case 0:
feedTableView.reloadData()
case 1:
feedTableView.reloadData()
case 2:
feedTableView.reloadData()
case 3:
feedTableView.reloadData()
default :
break
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if segmentedControl.selectedSegment == 0
{
return 0
}
else
if segmentedControl.selectedSegment == 1
{
return materialListObjects.count
}
else
if segmentedControl.selectedSegment == 2
{
return assignmentExamAndQuizListObjects.count
}
else
{
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if segmentedControl.selectedSegment == 0
{
let cell = Bundle.main.loadNibNamed("TypeOneCell", owner: self, options: nil)?.first as! TypeOneCell
return cell
}
else
if segmentedControl.selectedSegment == 1
{
if materialListObjects[indexPath.row].type == "file"
{
let cell = Bundle.main.loadNibNamed("materialCellOne", owner: self, options: nil)?.first as! materialCellOne
cell.materialNameLabel.text = materialListObjects[indexPath.row].title
let image = materialListObjects[indexPath.row].title
cell.contentImage.image = image.documentType(givenType: image)
return cell
}else
{
let cell = Bundle.main.loadNibNamed("materialCellTwo", owner: self, options: nil)?.first as! materialCellTwo
cell.materialNameLabel.text = materialListObjects[indexPath.row].title
cell.contentImage.image = #imageLiteral(resourceName: "material_hyperlink")
return cell
}
}
else
if segmentedControl.selectedSegment == 2
{
let cell = Bundle.main.loadNibNamed("CourseWorkCell", owner: self, options: nil)?.first as! CourseWorkCell
print("assignment title : \(assignmentExamAndQuizListObjects[indexPath.row].title)")
cell.titleLabel.text = assignmentExamAndQuizListObjects[indexPath.row].title
if assignmentExamAndQuizListObjects[indexPath.row].type == ""
{
cell.contentImage.image = #imageLiteral(resourceName: "assignment_large")
}else
{
cell.contentImage.image = #imageLiteral(resourceName: "exam_inline")
}
var time = assignmentExamAndQuizListObjects[indexPath.row].start
time = time.dateRange(dateString: time)
time = time.days(givenDate: time)
cell.timeLabel.text = time
return cell
}
else
if segmentedControl.selectedSegment == 3
{
let cell = Bundle.main.loadNibNamed("TypeOneCell", owner: self, options: nil)?.first as! TypeOneCell
return cell
}
else
{
let cell = Bundle.main.loadNibNamed("TypeOneCell", owner: self, options: nil)?.first as! TypeOneCell
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if segmentedControl.selectedSegment == 2
{
return 70
}
else
{
return 100
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect.zero)
let label = UILabel(frame: CGRect(x: 8, y: 8, width: 150, height: 20))
view.addSubview(label)
label.font = UIFont(name: "Lato-Heavy", size: 17)
if segmentedControl.selectedSegment == 1
{
switch section {
case 0:
label.text = "All Materials"
case 1:
label.text = "From Your Courses"
default:
break
}
}
else
if segmentedControl.selectedSegment == 2
{
switch section {
case 0:
label.text = "All CourseWork"
case 1:
label.text = "From Your Courses"
default:
break
}
}
else
{
}
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
//How to perform from here
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if segmentedControl.selectedSegment == 1
{
let storyboard = UIStoryboard(name: "Main", bundle : nil)
let nextViewController = storyboard.instantiateViewController(withIdentifier: "QuickLook") as! QuickLook
if materialListObjects[indexPath.row].type == "url"
{
nextViewController.id = materialListObjects[indexPath.row].body
nextViewController.type = "url"
}
else
{
nextViewController.id = materialListObjects[indexPath.row].id
}
nextViewController.course_id = String(describing: materialListObjects[indexPath.row].course_id)
let naviControl = UINavigationController(rootViewController: nextViewController)
naviControl.pushViewController(nextViewController, animated: true)
}
}
}
I have created a similar scenario as yours and this is how you can get it working.
1. View Hierarchy
I have used tag property to uniquely identify both UITableViews, i.e.
Outer tableView tag = 0
Inner tableView tag = 1
2. Now implement UITableViewDataSource, UITableViewDelegate methods for both the tableViews. Set the dataSource and delegate of both the tableViews as the ViewController.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if tableView.tag == 0
{
return 1
}
else if tableView.tag == 1
{
return 5
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if tableView.tag == 0
{
return tableView.dequeueReusableCell(withIdentifier: "outercell", for: indexPath)
}
else if tableView.tag == 1
{
return tableView.dequeueReusableCell(withIdentifier: "innercell", for: indexPath)
}
return UITableViewCell()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if tableView.tag == 1
{
//TODO: Write your code for navigating to another ViewController here
print("Inner cell tapped")
}
}
Edit:
In the Interface Builder, you can find a tag attribute corresponding to each element in the attributes inspector, i.e.
For outer tableView set it to 0 and for inner tableView set it to 1.
Let me know if you still face any issues. Happy Coding..🙂
Use performSegue(withIdentifier: "ViewController", sender: self); in didSelectrow method of you tableView2.

Swift 3.0 multiple selection with select all cell

I have added data in table view and I have manually added "select all" option to the list at first position, now when the user selects the first option which is 'select all' then the person manually option "Select all" is not selected. Select all, click then work all person or deselect working but signal selection all the person not working "Select all"
I have tried the code below but it's not working so can any one help me to solve this?
var unchecked:Bool = true
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell = ObjTableview.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SelectUserCell
// set the text from the data model
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.lblStudentName.text = getStudentName[indexPath.row]
if UnAll == "unselect" {
if indexPath.row == 0 {
cell.btnCheckbox.setImage(UIImage(named: "unSelectedItem"), for: .normal)
}
if indexPath.row == Int(selectedNumber) {
cell.btnCheckbox.setImage(UIImage(named: "unSelectedItem"), for: .normal)
}
if indexPath.row == Int(unSelectNumber) {
//var j = "\(i)"
cell.btnCheckbox.setImage(UIImage(named: "selectedItem"), for: .normal)
}
}else
{
if(unchecked){
cell.btnCheckbox.setImage(UIImage(named: "unSelectedItem"), for: .normal)
}
else{
cell.btnCheckbox.setImage(UIImage(named: "selectedItem"), for: .normal)
}
}
return cell
}
var UnAll = ""
var selectedNumber = ""
var unSelectNumber = ""
var checkselect:Bool = true
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UnAll.removeAll()
selectedNumber.removeAll()
unSelectNumber.removeAll()
if(indexPath.row == 0){
btnCheckBoxClick(sender: UIButton())
}else
{
UnAll = "unselect"
btnCheckBoxClick(sender: UIButton())
if checkselect {
selectedNumber = "\(indexPath.row)"
checkselect = false
}else
{
unSelectNumber = "\(indexPath.row)"
checkselect = true
}
print("the selected index is : \(indexPath.row)")
}
}
#IBAction func btnCheckBoxClick(_ sender: Any) {
if(unchecked){
unchecked = false
}
else{
unchecked = true
}
ObjTableview.reloadData()
}
Create a struct for model data with a Bool property. You can modify this property by cell selection.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var allCharacters:[Character] = []
override func viewDidLoad() {
super.viewDidLoad()
allCharacters = [Character(name: "All"),Character(name: "Luke Skywalker"),Character(name: "Leia Organa"),Character(name: "Advik Shah"),Character(name: "Aarav Modi")]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allCharacters.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
if cell == nil{
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell")
}
cell?.textLabel?.text = allCharacters[indexPath.row].name
if allCharacters[indexPath.row].isSelected
{
cell?.accessoryType = .checkmark
}
else
{
cell?.accessoryType = .none
}
cell?.selectionStyle = .none
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0
{
allCharacters[indexPath.row].isSelected = !allCharacters[indexPath.row].isSelected
for index in allCharacters.indices
{
allCharacters[index].isSelected = allCharacters[indexPath.row].isSelected
}
}
else
{
allCharacters[indexPath.row].isSelected = !allCharacters[indexPath.row].isSelected
if allCharacters.dropFirst().filter({ $0.isSelected }).count == allCharacters.dropFirst().count
{
allCharacters[0].isSelected = true
}
else
{
allCharacters[0].isSelected = false
}
}
tableView.reloadData()
}
}
struct Character
{
var name:String
// var otherDetails
var isSelected:Bool! = false
init(name:String) {
self.name = name
}
}
Creating Array of Struct objects from array of dictionary
let SubjectArray = json["students"] as! [[String:Any]]
allCharacters = SubjectArray.map({ Character(name: $0["studentName"] as! String) })
allCharacters.insert(Character(name:"All"), at: 0)
I like #Pranil's suggestion of using a separate section for the "All" row, so I have stolen that.
You can use an NSMutableIndexSet for tracking the selected rows. This is simpler than having to create a new struct or array of booleans or something. The only thing you do need to be aware of is if your tableview allows row reordering then the index set needs to be adjusted accordingly.
Here is my implementation. The "all" state is determined by the number of selected rows being equal to the number of rows in the data source array.
I have just used simple table view accessories for the checkmarks, but I am sure you can see how to adopt your image based approach in cellForRow(at:)
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableview: UITableView!
let names: [String]? = ["Luke Skywalker","Leia Organa","Advik Shah","Aarav Modi"]
var selectedRows = NSMutableIndexSet()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let names = self.names else {
return 0
}
return 0 == section ? 1 : names.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)
var text: String
var accessory = UITableViewCellAccessoryType.none
if 0 == indexPath.section {
text = "All"
if self.selectedRows.count == self.names!.count {
accessory = .checkmark
}
} else {
text = names![indexPath.row]
if selectedRows.contains(indexPath.row) {
accessory = .checkmark
}
}
cell.textLabel!.text = text
cell.accessoryType = accessory
return cell
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if indexPath.section == 0 {
if self.selectedRows.count == self.names!.count {
self.selectedRows = NSMutableIndexSet()
} else {
self.selectedRows = NSMutableIndexSet(indexesIn: NSRange(location: 0, length: self.names!.count))
}
tableView.reloadData()
} else {
self.selectedRows.contains(indexPath.row) ? self.selectedRows.remove(indexPath.row) : self.selectedRows.add(indexPath.row)
let rows = [IndexPath(row: 0, section: 0), indexPath]
tableView.reloadRows(at: rows, with: .none)
}
return nil
}
}
I think you are using only one section in the table view. I suggest you use two sections in the table view, so that first section will contain only one row (Select All) and the second section will contain other options. When you click on Select All, that is in the first row of the first section you can make all the rows in the second section as selected while reloading the table view.
// MARK: - struct for cell item
struct CellItem {
var name : String
var isSelected:Bool! = false
init(name: String) {
self.name = name
}
}
class ViewController: UITableViewController {
#IBOutlet var viewTable: UITableView!
// Declare a boolean varaible to toggle the checkbox in the first section of table view
var isSelectAllSelected : Bool = false
var cellData: [CellItem] = []
override func viewDidLoad() {
super.viewDidLoad()
cellData = [CellItem(name: "Luke Skywalker"),CellItem(name: "Leia Organa"),CellItem(name: "Advik Shah"),CellItem(name: "Aarav Modi")]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
}
else
{
return cellData.count
}
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0
}
// MARK: - Table view delegates
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = TableCell()
cell.selectionStyle = .none
if indexPath.section == 0 {
cell.textLabel?.text = "Select All"
if isSelectAllSelected{
cell.accessoryType = .checkmark
}
else{
cell.accessoryType = .none
}
}
else
{
cell.textLabel?.text = cellData[indexPath.row].name
if cellData[indexPath.row].isSelected{
cell.accessoryType = .checkmark
}
else{
cell.accessoryType = .none
}
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0
{
cellData[indexPath.row].isSelected = !cellData[indexPath.row].isSelected
isSelectAllSelected = cellData[indexPath.row].isSelected
for index in cellData.indices
{
cellData[index].isSelected = cellData[indexPath.row].isSelected
}
}
else
{
cellData[indexPath.row].isSelected = !cellData[indexPath.row].isSelected
if cellData.filter({ $0.isSelected }).count == cellData.count
{
isSelectAllSelected = true
}
else
{
isSelectAllSelected = false
}
}
viewTable.reloadData()
} }
Hello u can take cheboxbutton action method inside view controller with addtarget method and assign tag indexpath.row so u can easily get the indexpath. from below code u can get the idea.
class ViewController:UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var ObjTableview: UITableView!
var arrStudent = ["1","2","3","4","5"]
var arrSelectedStudent :[Int] = []
var selectAll:Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrStudent.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell = ObjTableview.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SelectUserCell
// set the text from the data model
cell.selectionStyle = UITableViewCellSelectionStyle.none
// cell.lblStudentName.text = getStudentName[indexPath.row]
cell.lblStudentName.text = arrStudent[indexPath.row]
cell.btnCheckbox.tag = indexPath.row
cell.btnCheckbox.addTarget(self, action:#selector(btnCheckBoxClick(sender:)), for: .touchUpInside)
if selectAll {
cell.btnCheckbox.setImage(UIImage(named: "selectedItem"), for: .normal)
}else{
if arrSelectedStudent.contains(indexPath.row){
cell.btnCheckbox.setImage(UIImage(named: "selectedItem"), for: .normal)
}else{
cell.btnCheckbox.setImage(UIImage(named: "unSelectedItem"), for: .normal)
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func btnCheckBoxClick(sender: UIButton) {
if sender.tag == 0{
selectAll = true
}else
{
selectAll = false
if let index = arrSelectedStudent.index(of: sender.tag) {
arrSelectedStudent.remove(at: index)
}else{
arrSelectedStudent.append(sender.tag)
}
}
ObjTableview.reloadData()
}}

Resources