I want to implement the different expand/collapse section in tableView and each sections contain the different kind of data.I'm getting the index of section when user click on button. Now i want when user click on button section collapse and expand.Initially first time all sections data should be hidden when user first time comes. See my code and screenshot.
ViewController Code:
enum ProfileItems: Int{
case namePicture = 0
case about = 1
}
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var items:[ProfileItems] = [.namePicture, .about]
var profile:[Profiles] = [
Profiles(name: "Ki", profileImage: "slide2"),
Profiles(name: "Kaleem", profileImage: "slide3"),
Profiles(name: "Jameel", profileImage: "slide2")
]
var about: [About] = [
About(title: "Johns"),
About(title: "Dons")
]
var isHiddenSection: Bool = false {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
registerCell()
}
func registerCell() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: String(describing: PictureTvCell.self), bundle: .main), forCellReuseIdentifier: String(describing: PictureTvCell.self))
tableView.register(UINib(nibName: String(describing: AboutTVCell.self), bundle: .main), forCellReuseIdentifier: String(describing: AboutTVCell.self))
tableView.register(UINib(nibName: String(describing: HeaderTVCell.self), bundle: .main), forCellReuseIdentifier: String(describing: HeaderTVCell.self))
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: HeaderTVCell.self)) as? HeaderTVCell else {return UIView()}
switch items[section]{
case .namePicture:
cell.lableName.text = "Info"
case .about:
cell.lableName.text = "Collection"
}
cell.buttonCollpase.tag = items[section].rawValue
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch items[section] {
case .namePicture:
return profile.count
case .about:
return about.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch items[indexPath.section] {
case .namePicture:
return configurePictureCell(tableView, cellForRowAt: indexPath)
case .about:
return configureAboutCell(tableView, cellForRowAt: indexPath)
}
}
func configurePictureCell(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PictureTvCell.self)) as? PictureTvCell else {return UITableViewCell()}
cell.profiles = self.profile[indexPath.row]
return cell
}
func configureAboutCell(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: AboutTVCell.self)) as? AboutTVCell else {return UITableViewCell()}
cell.about = self.about[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40
}
}
extension ViewController: CollpaseSection {
func getIndex(_ index: Int) {
switch items[index] {
case .namePicture:
print("picture")
case .about:
print("about")
tableView.reloadData()
}
}
}
TableViewHederCell Code:
protocol CollpaseSection:AnyObject {
func getIndex(_ index: Int)
}
class HeaderTVCell: UITableViewCell {
#IBOutlet weak var lableName: UILabel!
#IBOutlet weak var buttonCollpase: UIButton!
weak var delegate: CollpaseSection?
#IBAction func didTapCollapse(_ sender: UIButton) {
delegate?.getIndex(sender.tag)
}
}
Screenshot current output:
enter image description here
Related
Right now I am trying to move information from my goal cell into a new table view cell, and am having difficulty getting the cell to display.
Here is the code for my goal cell.
import UIKit
class GoalsViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var Goals: [String] = ["goal 1", "goal 2", "goal 3"]
let theEmptyModel: [String] = ["No data in this section."]
var valueToPass = ""
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func showGoalSelected() {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
let popUp = GoalSelectedPopUp()
self.view.addSubview(popUp)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "GoalConversationsCell_1") {
let viewController = segue.destination as! ActiveGoalsViewController
viewController.Goals.append([valueToPass])
}
}
}
extension GoalsViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Goals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "GoalCell_1", for: indexPath)
cell.textLabel?.text = Goals[indexPath.row]
cell.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
cell.textLabel?.numberOfLines = 3
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
valueToPass = Goals[indexPath.row]
performSegue(withIdentifier: "activeGoalsSegue", sender: self)
Goals.remove(at: indexPath.row)
if Goals.count != 0 {
showGoalSelected()
} else {
Goals.append(contentsOf: theEmptyModel)
}
tableView.reloadData()
}
}
Here is the goal cells storyboard with the push segue connecting it to the other table view.
That other table view is shown below.
Here is the code for this new tableview.
import UIKit
class ActiveGoalsViewController: UIViewController {
#IBOutlet weak var goalTableView: UITableView!
let sections: [String] = ["Mark as Complete:", "History:"]
var goals: [[String]] = [[], []]
let theEmptyModel: [String] = ["No data in this section."]
extension ActiveGoalsViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Goals[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TodayGoalViewCell_1", for: indexPath) as? GoalTableViewCell
cell?.goalLabel.text = Goals[indexPath.section][indexPath.row]
cell?.cellDelegate = self
cell?.index = indexPath
return cell!
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return Goals.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
if Goals[0] != theEmptyModel {
Goals[1].append(Goals[0][indexPath.row])
if Goals[1].first!.contains("No data in this section.") {
Goals[1].removeFirst()
}
Goals[0].remove(at: indexPath.row)
if Goals[0].count == 0 {
Goals[0].append(contentsOf: theEmptyModel)
}
tableView.reloadData()
}
}
}
Once the goal is selected, it sends me to the new storyboard, but this new view does not display the goal that was just added. Can someone help me figure out why this isn't working? Thanks.
I think in the second view controller you need to access the "goals" variable with a lower case g rather then the "Goals" variable with an upper case G.
There are three button in a UIView and a UITableView below the UIView i want to load xib on click on button. if first button is click load new xib if second button is click load approved xib if third button is click load rejected xib
#IBOutlet weak var newButton: UIButton!
#IBOutlet weak var approvedButton: UIButton!
#IBOutlet weak var rejectedButton: UIButton!
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var approvedDownLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
approvedDownLabel.isHidden = true
rejectedDownLbl.isHidden = true
addNewTblview.dataSource = self
addNewTblview.delegate = self
newButton.isSelected = true
self.addNewTblview.register(UINib(nibName: "AddInstructorNewCell", bundle: nil), forCellReuseIdentifier: "AddInstructorNewCell")
self.addNewTblview.register(UINib(nibName: "ApprovedCell", bundle: nil), forCellReuseIdentifier: "ApprovedCell")
self.addNewTblview.register(UINib(nibName: "RejectedCell", bundle: nil), forCellReuseIdentifier: "RejectedCell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if newButton.isSelected {
let cell = self.addNewTblview.dequeueReusableCell(withIdentifier: "AddInstructorNewCell", for: indexPath) as! AddInstructorNewCell
return cell
} else if approvedButton.isSelected {
let cell = self.addNewTblview.dequeueReusableCell(withIdentifier: "ApprovedCell", for: indexPath) as! ApprovedCell
return cell
} else {
let cell = self.addNewTblview.dequeueReusableCell(withIdentifier: "RejectedCell", for: indexPath) as! RejectedCell
return cell
}
// return UITableViewCell()
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if newButton.isSelected {
return 170
} else if approvedButton.isSelected{
return 165
}else {
return 178
}
// return 170
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
First you create tree cell class with .xib.
#IBAction func btnNewClick(_ sender:UIButton)
{
self.approvedButton.isSelected = false
self.rejectedButton.isSelected = false
self.Yourtbl.reloadData()
}
set action btnapprovedClick button
#IBAction func btnapprovedClick(_ sender:UIButton)
{
self.approvedButton.isSelected = true
self.rejectedButton.isSelected = false
self.newButton.isSelected = false
self.Yourtbl.reloadData()
}
set action btnrejectedClick button
#IBAction func btnrejectedClick(_ sender:UIButton)
{
self.approvedButton.isSelected = false
self.rejectedButton.isSelected = true
self.newButton.isSelected = false
self.Yourtbl.reloadData()
}
Why don't you use enum? See the code below for further reference.
import UIKit
enum ViewType {
case new, approved, rejected
}
class ViewController {
private var viewType: ViewType = .new
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch viewType {
case .new :
let cell = self.addNewTblview.dequeueReusableCell(withIdentifier: "AddInstructorNewCell", for: indexPath) as! AddInstructorNewCell
return cell
case .approved:
let cell = self.addNewTblview.dequeueReusableCell(withIdentifier: "ApprovedCell", for: indexPath) as! ApprovedCell
return cell
case .rejected:
let cell = self.addNewTblview.dequeueReusableCell(withIdentifier: "RejectedCell", for: indexPath) as! RejectedCell
return cell
}
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
switch viewType {
case .new:
return 170
case .approved:
return 165
case .rejected:
return 178
}
}
#IBAction func btnNewClick(_ sender:UIButton)
{
viewType = .new
tableView.reloadData()
}
#IBAction func btnapprovedClick(_ sender:UIButton)
{
viewType = .approved
tableView.reloadData()
}
#IBAction func btnrejectedClick(_ sender:UIButton)
{
viewType = .rejected
tableView.reloadData()
}
}
I have a master-detail view application in which MasterView's table cell is a Xib cell. I also have two TableViews in DetailViewController placed side by side as shown below:
Master and Detail Views
Now my problem is I am unable to populate DetailView's Table View when I am clicking on Master's TableViewCell. I am pasting my code below:
class MasterViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var devarlViewController: DetailViewController? = nil
var objects = [Any]()
var statesList = ["AL", "GA", "AK", "AR", "AZ", "CA", "CO", "CT", "CO"]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "cell")
// Do any additional setup after loading the view, typically from a nib.
}
// MARK: - Table View
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return statesList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel!.text = statesList[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
DetailViewController.sharedDetail.selectedState = (self.tableView(tableView, cellForRowAt: indexPath).textLabel?.text)!
DetailViewController.sharedDetail.selectedCity = ""
}
}
class DetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
static let sharedDetail = DetailViewController()
#IBOutlet weak var CitiesTable: UITableView!
#IBOutlet weak var PlacesTable: UITableView!
var selectedState: String = ""{
didSet{
CitiesTable.reloadData()
}
}
var selectedCity: String = ""
var citiesStates: [String:[String]] = [ "AL": ["Auburn", "Montgomery", "Birmingham"]
]
var placesCities: [String:[String]] = [ "Auburn": ["Dominos", "Pizzahut", "McDonalds"],
"Birmingham": ["iHop", "Coldstone", "WaffleHouse"]
]
var cities: [String] = []
var places: [String] = []
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 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(tableView == CitiesTable)
{
if(selectedState != "")
{
cities = citiesStates[selectedState]!
return cities.count
}
}
else
{
if(selectedCity != "")
{
places = placesCities[selectedCity]!
return places.count
}
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(tableView == CitiesTable)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "citiesCell", for: indexPath)
cell.textLabel?.text = cities[indexPath.row]
return cell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: "placesCell", for: indexPath)
cell.textLabel?.text = cities[indexPath.row]
return cell
}
}
}
I have added a protocol in masterviewcontroller shown below
protocol MasterSelectionDelegate: class {
func itemSelected(_ item: AnyObject)
}
then, as splitviewcontroller sits as a rootviewcontroller.
In app delegate or during creation of Split View Controller. I have added below line
masterViewController.delegate = detailViewController
In didSelectRowAtIndexPath of master ViewController I have called it this:
`override func tableView(tableView: UITableView,
didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedItem = self.arrString[indexPath.row]
self.delegate?.itemSelected(selectedItem)
}`
Finally you have to implement it in the delegate in detail view controller
func itemSelected(_ item: AnyObject) {
itemSelected = item as? Array<String>
tableview.reloadData()
}
I am using one UITableView to select the country with tick mark. But when I move to other screen and when I come back my check mark is invisible. It seems like the country what I am selecting is fine, But after I move to other screen an come back, The selected tick mark is not there. How to do that in swift.
my code :
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var saveBtn: UIButton!
var languageName : String = String()
var option : [String] = ["English","हिंदी"]
var option1 : [String] = []
let availableLanguages = Localize.availableLanguages()
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
for language in availableLanguages {
option1.append(language)
let displayName = Localize.displayNameForLanguage(language)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
//MARK: - TableView
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return option1.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = option[indexPath.row]
if option1[indexPath.row] == languageName{
cell.accessoryType = UITableViewCellAccessoryType.checkmark
}else{
cell.accessoryType = UITableViewCellAccessoryType.none
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
languageName = option1[indexPath.row]
self.tableView.reloadData()
}
#IBAction func saveButtonPressed(_ sender: Any) {
Localize.setCurrentLanguage(languageName)
if let appdelegate = UIApplication.shared.delegate as? AppDelegate {
appdelegate.showHomeLandingScreen()
}
}
1) create another array of selected items and save it there are so many options eg. UserDefaults.standard
2) then compare with option1[indexPath.row]
example
UserDefaults.standard.set(selectedLanguageArray, forKey: "selectedLanguageArray")
UserDefaults.standard.synchronize()
Then get it by
UserDefaults.standard.value(forKey: "selectedLanguageArray")
create another array of selected items
here option1[indexPath.row] compare this element with all element of another array
Here you go:-
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var saveBtn: UIButton!
var languageName : String = String()
var option : [String] = ["English","हिंदी"]
var selectedlang: [String] = []
let availableLanguages = Localize.availableLanguages()
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
for language in availableLanguages {
option1.append(language)
let displayName = Localize.displayNameForLanguage(language)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
//MARK: - TableView
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return option1.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = option[indexPath.row]
for (index,element) in selectedlang.enumerated(){
if element == option[indexPath.row]{
cell.accessoryType = UITableViewCellAccessoryType.checkmark
}else{
cell.accessoryType = UITableViewCellAccessoryType.none
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
languageName = option1[indexPath.row]
for (index,element) in newArr.enumerated(){
if element == languageName{
selectedlang.remove(at: index)
}else{
selectedlang.append(languageName)
}
}
self.tableView.reloadData()
}
#IBAction func saveButtonPressed(_ sender: Any) {
Localize.setCurrentLanguage(languageName)
if let appdelegate = UIApplication.shared.delegate as? AppDelegate {
appdelegate.showHomeLandingScreen()
}
}
}
Please use below code which i corrected and tested, It stores last changed language and will get it even when you move other screen an come back
// ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var saveBtn: UIButton!
var languageName : String?
var option : [String] = ["English","हिंदी","French","Dutch"] //Your languages displays in table view
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
languageName = UserDefaults.standard.value(forKey: "MyselectedLanguage") as? String //Your last selected language fetch
self.tableView.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
//MARK: - TableView
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return option.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = option[indexPath.row]
if option[indexPath.row] == languageName{
cell.accessoryType = UITableViewCellAccessoryType.checkmark
}else{
cell.accessoryType = UITableViewCellAccessoryType.none
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
languageName = option[indexPath.row]
self.tableView.reloadData()
}
#IBAction func saveButtonPressed(_ sender: Any) {
UserDefaults.standard.set(languageName, forKey: "MyselectedLanguage")
UserDefaults.standard.synchronize()
if let appdelegate = UIApplication.shared.delegate as? AppDelegate {
appdelegate.showHomeLandingScreen()
}
}
}
See the reference Image:
Here is a similar code that I use to save News category selections, should help you with your problem.
Saves multiple values that are checked.
class ViewController {
var selectedCategoriesArray = [Any]()
private var appSettings: UserDefaults?
override func viewDidLoad() {
super.viewDidLoad()
appSettings = UserDefaults.standard
loadNewsSelectedCategories()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: "cellNewsCategory", for: indexPath)
// Configure the cell...
cell?.textLabel?.text = "\(newsCategoriesArray[indexPath.row])"
let cellText: String? = cell?.textLabel?.text
for lbl: String in selectedNewsCategories {
if (cellText == lbl) {
cell?.accessoryType = .checkmark
break
}
else {
cell?.accessoryType = []
}
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let selectedCell: UITableViewCell? = tableView.cellForRow(at: indexPath)
let selectedCategory: String? = selectedCell?.textLabel?.text
if tableView.cellForRow(at: indexPath)?.accessoryType == [] {
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
print("\(selectedCategory)")
selectedCategoriesArray += selectedNewsCategories
selectedCategoriesArray.append(selectedCategory)
let categories: [Any] = NSOrderedSet(selectedCategoriesArray).array()
print("+categories:\n \(categories)")
appSettings["selectedNewsCategories"] = categories
}
else if tableView.cellForRow(at: indexPath)?.accessoryType == .checkmark {
tableView.cellForRow(at: indexPath)?.accessoryType = []
print("\(selectedCategory)")
loadNewsSelectedCategories()
selectedCategoriesArray += selectedNewsCategories
selectedCategoriesArray.remove(at: selectedCategoriesArray.index(of: selectedCategory)!)
var categories: [Any] = NSOrderedSet(selectedCategoriesArray).array()
print("-categories:\n \(categories)")
appSettings["selectedNewsCategories"] = categories
}
else {
tableView.cellForRow(at: indexPath)?.accessoryType = []
}
appSettings.synchronize()
}
func loadNewsSelectedCategories() {
selectedNewsCategories = [Any]()
selectedNewsCategories = appSettings["selectedNewsCategories"]
}
I'm trying to build a pretty complex table. One cell of this table imports another table. In this imported table I show different rows depending on the situation. The imported table and all the importable table cells are organized into own nib files with own controllers for every cell. As a good programmer I'm trying to use dependency injection throughout the project. The problem now is that when I use the usual way of registering the nibs in viewDidLoad()
let cellNib = UINib(nibName: "BatchConsumptionCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionCell")
and then use them as dequeueReusableCell(withIdentifier: for:) in tableView(tableView: cellForRowAt:)
let batchConsumptionCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionCell", for: indexPath) as! BatchConsumptionCell
batchConsumptionCell.setConsumption(consumption: consumption)
return batchConsumptionCell
I'm not able to inject the dependencies in time.
In the BatchConsumptionCell all I do in tableView(tableView: cellForRowAt:) works fine. This function is called after dequeueReusableCell(withIdentifier: for:) is executed. But as soon as I'm trying to make the tableView(tableView: numberOfRowsInSection:) dynamic I run into problems. This functions seems to be called before dequeueReusableCell(withIdentifier: for:) so the dependencies are not injected at this point.
I tried to override the init(nibName: bundle:) initializer in my BatchConsumptionCell, but this is a UITableViewCell so I was not able to override this.
How do I approach this? Is there any way I could inject the dependencies as the nib and its controller are initialized? Or do I organize my cells all wrong? Any ideas would be much appreciated.
For more clarity here is my code:
ConsumptionDetailViewController
import UIKit
class ConsumptionDetailViewController: UITableViewController {
// MARK: - Properties
var moc: NSManagedObjectContext!
var consumption: Consumption!
// MARK: - Outlet Properties
#IBOutlet weak var labelDate: UILabel!
#IBOutlet weak var labelTime: UILabel!
...
// MARK: - Default Methods
override func viewDidLoad() {
super.viewDidLoad()
let cellNib = UINib(nibName: "BatchConsumptionCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionCell")
updateTableFields(selectedConsumption: consumption)
}
// MARK: - UI Update Methods
func updateTableFields(selectedConsumption: Consumption) {
labelId.text = selectedConsumption.wtId
...
}
// MARK: - BatchAddEditDelegate Methods
func didFinishEditing(consumption: Consumption) {
updateTableFields(selectedConsumption: consumption)
}
// MARK: - TableView Methods
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
...
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
...
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
...
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
...
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 1 && indexPath.row == 0 {
let batchConsumptionCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionCell", for: indexPath) as! BatchConsumptionCell
batchConsumptionCell.setConsumption(consumption: consumption)
return batchConsumptionCell
}
return super.tableView(tableView, cellForRowAt: indexPath)
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
...
}
}
BatchConsumptionCell
import UIKit
class BatchConsumptionCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {
var consumption: Consumption!
var batchConsumptionCount: Int!
#IBOutlet weak var tableView: UITableView!
override func awakeFromNib() {
super.awakeFromNib()
var cellNib = UINib(nibName: "BatchConsumptionBasicCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionBasicCell")
cellNib = UINib(nibName: "BatchConsumptionMultiCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionMultiCell")
self.tableView.delegate = self
self.tableView.dataSource = self
}
func setConsumption(consumption: Consumption) {
self.consumption = consumption
self.batchConsumptionCount = consumption.batchConsumptions?.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if batchConsumptionCount == 1 { // <-- This does not work
return 3
} else {
return batchConsumptionCount
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if batchConsumptionCount == 1 { // <-- This works fine
let batchConsumptionBasicCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionBasicCell", for: indexPath) as! BatchConsumptionBasicCell
let bc = consumption.batchConsumptions?.allObjects[0] as! BatchConsumption
if indexPath.row == 0 {
batchConsumptionBasicCell.configure(title: "Batch", detail: (bc.batch?.wtId)!)
} else if indexPath.row == 1 {
batchConsumptionBasicCell.configure(title: "Weight", detail: String(describing: bc.weight!))
} else if indexPath.row == 2 {
batchConsumptionBasicCell.configure(title: "Price", detail: String(format:"%.2f", bc.price))
}
} else if batchConsumptionCount >= 2 {
let batchConsumptionMultiCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionMultiCell", for: indexPath) as! BatchConsumptionMultiCell
return batchConsumptionMultiCell
}
return UITableViewCell()
}
}
See the comments // <-- This works fine and
// <-- This does not work in BatchConsumptionCell
You should also register nib to the BatchConsumptionCell class, and implement necessary table view's data source & delegate methods:
class BatchConsumptionCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var consumption: Consumption!
var batchConsumptionCount: Int!
override func awakeFromNib() {
super.awakeFromNib()
var cellNib = UINib(nibName: "BatchConsumptionBasicCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionBasicCell")
cellNib = UINib(nibName: "BatchConsumptionMultiCell", bundle: nil)
tableView.register(cellNib, forCellReuseIdentifier: "BatchConsumptionMultiCell")
self.tableView.delegate = self
self.tableView.dataSource = self
}
func setConsumption(consumption: Consumption) {
self.consumption = consumption
self.batchConsumptionCount = consumption.batchConsumptions?.count
tableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return consumption == nil ? 0 : 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if batchConsumptionCount == .some(1) { return 3 }
return batchConsumptionCount ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch batchConsumptionCount {
case .some(1):
let batchConsumptionBasicCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionBasicCell", for: indexPath) as! BatchConsumptionBasicCell
let bc = consumption.batchConsumptions?.allObjects[0] as! BatchConsumption
if indexPath.row == 0 {
batchConsumptionBasicCell.configure(title: "Batch", detail: (bc.batch?.wtId)!)
} else if indexPath.row == 1 {
batchConsumptionBasicCell.configure(title: "Weight", detail: String(describing: bc.weight!))
} else if indexPath.row == 2 {
batchConsumptionBasicCell.configure(title: "Price", detail: String(format:"%.2f", bc.price))
}
return batchConsumptionBasicCell
default:
let batchConsumptionMultiCell = tableView.dequeueReusableCell(withIdentifier: "BatchConsumptionMultiCell", for: indexPath) as! BatchConsumptionMultiCell
return batchConsumptionMultiCell
}
}
}