Swift: Multiple Custom TableViewCells from XIB files - ios

I am following this tutorial from Jared Davidson to implement multiple CustomTableViewCells with XIB files in my app. I have these files in my Xcode project:.
I have a TextElement: and
I have an ImageElement:
I want to test this with offline data to implement Firebase after this is working. This is my Home.swift data struct:
import Foundation
import FirebaseDatabase
struct Home {
var key:String!
let itemRef:FIRDatabaseReference?
var userUID:String!
var user:String!
// Home Element Cell Content
var elementSortNumber:Int!
var elementCellType:String!
var referenceElementID:String!
var databaseVersion:String!
init (key:String = "",
uid:String,
user:String,
elementSortNumber:Int,
elementCellType:String,
referenceElementID:String,
databaseVersion:String) {
// General (Security tracking)
self.key = key
self.itemRef = nil
self.userUID = uid
self.user = user
// Home Element Cell Content
self.elementSortNumber = elementSortNumber
self.elementCellType = elementCellType
self.referenceElementID = referenceElementID
}
init (snapshot:FIRDataSnapshot) {
// General (Security tracking)
key = snapshot.key
itemRef = snapshot.ref
if let addedByUser = snapshot.value as? NSDictionary, let _temp = addedByUser["User"] as? String {
user = _temp
} else {
user = ""
}
// Home Element Cell Content
if let homeElementSortNumber = snapshot.value as? NSDictionary, let _temp = homeElementSortNumber["Title"] as? Int {
elementSortNumber = _temp
} else {
elementSortNumber = 50
}
if let homeElementCellType = snapshot.value as? NSDictionary, let _temp = homeElementCellType["Content"] as? String {
elementCellType = _temp
} else {
elementCellType = ""
}
if let homeElementID = snapshot.value as? NSDictionary, let _temp = homeElementID["Ref Element ID"] as? String {
referenceElementID = _temp
} else {
referenceElementID = ""
}
if let textDatabaseVersion = snapshot.value as? NSDictionary, let _temp = textDatabaseVersion["DB Version"] as? String {
databaseVersion = _temp
} else {
databaseVersion = ""
}
}
}
This is the code of my TableViewController:
import UIKit
class HomeTableViewController: UITableViewController {
var arrayOfCellData = [Home]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
arrayOfCellData =
[Home(key: "",
uid:"",
user:"",
elementSortNumber:1,
elementCellType:"TextElement",
referenceElementID:"123ABC",
databaseVersion:"1"),
Home(key: "",
uid:"",
user:"",
elementSortNumber:1,
elementCellType:"ImageElement",
referenceElementID:"QWERTZ",
databaseVersion:"1"),
Home(key: "",
uid:"",
user:"",
elementSortNumber:1,
elementCellType:"TextElement",
referenceElementID:"XYZ789",
databaseVersion:"1")]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// If I return 1 the app crashes and if I comment this function it also crashes.
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayOfCellData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if arrayOfCellData[indexPath.row].elementCellType == "TextElement" {
let textElementCell = Bundle.main.loadNibNamed("TextElementTableViewCell", owner: self, options: nil) as! TextElementTableViewCell
textElementCell.textElementTitleLabel.text = arrayOfCellData[indexPath.row].referenceElementID
return textElementCell
}
else if arrayOfCellData[indexPath.row].elementCellType == "ImageElement" {
let imageElementCell = Bundle.main.loadNibNamed("ImageElementTableViewCell", owner: self, options: nil) as! ImageElementTableViewCell
imageElementCell.imageElementImageView.image = UIImage(named: "placeholder")
return imageElementCell
}
else {
let textElementDefaultCell = Bundle.main.loadNibNamed("TextElementTableViewCell", owner: self, options: nil) as! TextElementTableViewCell
textElementDefaultCell.textElementTitleLabel.text = arrayOfCellData[indexPath.row].referenceElementID
return textElementDefaultCell
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if arrayOfCellData[indexPath.row].elementCellType == "TextElement" {
return 116
}
else if arrayOfCellData[indexPath.row].elementCellType == "ImageElement" {
return 275
}
else {
return 116
}
}
}
This is the problem: The simulator is empty as you can see in this image Why? How can I fix that?
I would really appreciate some help. Thank you.

Register the xib files as below in viewdidload:
tableView.register(UINib(nibName: "TextElementTableViewCell", bundle: Bundle.main), forCellReuseIdentifier: "TextElementTableViewCellIdentifier")
Then in cellForRowIndex path:Access cell using their identifier
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "TextElementTableViewCellIdentifier", for: indexPath) as! TextElementTableViewCell

Related

table view cannot be displayed on device

I am developing iOS app where I want to display table content one my device.but unable fetch and display...but contents are being displayed on console view.
I am using json getmethod() to fetch the details
also using view controller and view table and swift language
import UIKit
class UpdateSheetManagementViewController:
UIViewController, UITableViewDelegate, UITableViewDataSource, WebserviceDelegate {
//var FinalArray = [[String:Any]]()
// class func instantiateFromStoryboard() -> UpdateSheetManagementViewController {
// let storyboard = UIStoryboard(name: "Management", bundle: nil)
// return storyboard.instantiateViewController(withIdentifier: String(describing: self)) as! UpdateSheetManagementViewController
// }
#IBOutlet weak var managementTableView: UITableView!
var controllerType : String!
var array = [[String : Any]]()
override func viewDidLoad() {
super.viewDidLoad()
title = controllerType
updatesheetWebserviceCall()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// updatesheetWebserviceCall()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 320.0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell = tableView.dequeueReusableCell(withIdentifier: "UpdateSheetManagementTableViewCell", for: indexPath) as! UpdateSheetManagementTableViewCell
let cellIdentifier : String = "UpdateSheetManagementTableViewCell";
let cell : UpdateSheetManagementTableViewCell =
tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! UpdateSheetManagementTableViewCell
let dict = array[indexPath.section]
cell.classname.text = dict["Cls_Name"] as? String
cell.dateFrom.text = dict["Date_From"] as? String
cell.date_To.text = dict["Date_To"] as? String
cell.downloadButton.tag = indexPath.row
cell.downloadButton.addTarget(self, action: #selector(onDownloadButtonClicked(button:)), for: .touchUpInside)
return cell
}
func onDownloadButtonClicked(button : UIButton) {
let postion = button.tag
let data = array[postion]
let attachment = data["Attachment"] as? String
if(attachment != nil && attachment!.characters.count > 0){
let userid = UserDefaults.standard.string(forKey: USER_ID)
let finalString = "https://skillskool.mycit.co.in/PagesParentApp/Today-ClassNotes.aspx?FilePath=" + attachment! + "&UserId=" + userid! + "&PageName=UpdateSheet"
let url = URL(string: finalString)
if(url != nil){
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
}
}
}
func updatesheetWebserviceCall() {
let str = ""
let webClass = WebserviceClass()
webClass.delegate = self
webClass.fireRequest(functionName: "MgmtUpdateSheet.php", requestString: str, view: view)
}
func webserviceDidFinishWith(response: [String : Any], functionName: String) {
parseUpdateSheetWebservice(response: response)
}
func parseUpdateSheetWebservice(response : [String : Any]) {
let arr = response["homework"] as? [[String : Any]]
if(arr != nil){
// array.removeAll()
self.array.append(contentsOf: arr!)
// managementTableView.reloadData()
}
}
}
Add managementTableView.reloadData() after updating the data and make sure to set the delegate and datasource in either view controller or storyboard
Set tableview delegate and datasource in viewDidLoad and reload tableview in web service response.
// set tableview delegate and datasource in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
title = controllerType
managementTableView.dataSource = self
//managementTableView.delegate = self
updatesheetWebserviceCall()
}
// reload tableview upon web service response
func parseUpdateSheetWebservice(response : [String : Any]) {
let arr = response["homework"] as? [[String : Any]]
if(arr != nil){
// array.removeAll()
self.array.append(contentsOf: arr!)
self.managementTableView.reloadData()
/*
// or reload table using main queue, if you web service operation is in background queue
DispatchQueue.main.async {
self.managementTableView.reloadData()
}
*/
}
}
In the beginning, you need to set tableview dataSource and delegate and after loading data you also need to reload data.
self.managementTableView.delegate = self;
self.managementTableView.datasource = self;
Reload data:
self.array.append(contentsOf: arr!)
self.managementTableView.reloadData()
You have to set in viewDidLoad
managementTableView.dataSource = self
and un comment managementTableView.reloadData()
func parseUpdateSheetWebservice(response : [String : Any]) {
let arr = response["homework"] as? [[String : Any]]
if(arr != nil){
// array.removeAll()
self.array.append(contentsOf: arr!)
DispatchQueue.main.async {
managementTableView.reloadData() }
}
}
I think delegate and datasource connections are missed, please add the following code in viewDidLoad function:
self.managementTableView.delegate = self;
self.managementTableView.dataSource = self;

Get parent key in UITableview with firebase

I have my firebase database structured like this:
Snap (-KWLSAIh5WJvNJOkxBEr) {
beschrijving = "description";
image = "link to image";
title = "title";
}
Snap (-KWLSTak0H20X_2Qnanv) {
beschrijving = "description";
image = "link to image";
title = "title";
}
This is the code I am using to display this in a TableView:
import UIKit
import Firebase
class NieuwsTableViewController: UITableViewController {
var users = [UsersII]()
let cellId = "IdCell"
override func viewDidLoad() {
super.viewDidLoad()
fetchUser()
}
func fetchUser() {
Database.database().reference().child("Blog").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = UsersII(dictionary: dictionary)
self.users.append(user)
print(snapshot)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}, withCancel: nil)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> lllTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let user = users.reversed()[indexPath.row]
cell.textLabel?.text = user.name
return cell as! lllTableViewCell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let message = users.reversed()[indexPath.row]
guard let beschrijving = message.beschrijving else {
return
}
guard let image = message.plaatje else {
return
}
guard let titel = message.name else {
return
}
UserDefaults.standard.set(beschrijving, forKey: "nieuwsBeschrijving")
UserDefaults.standard.set(image,forKey: "nieuwsPlaatje")
UserDefaults.standard.set(titel, forKey: "nieuwsTitel")
self.performSegue(withIdentifier: "gotonews", sender: nil)
}
}
And I don't know if you will need this to answer this question but I'll also post the "UsersII" (defined as users just above the viewDidLoad method) in case this is needed to answer the question.
import UIKit
class UsersII: NSObject {
var name: String?
var beschrijving: String?
var plaatje: String?
init(dictionary: [String: Any]) {
self.name = dictionary["title"] as? String ?? ""
self.beschrijving = dictionary["beschrijving"] as? String ?? ""
self.plaatje = dictionary["image"] as? String ?? ""
}
}
so what I want to achieve is that if you click on one of the cells, you get the parent id of the article, so in this case that would be the "-KWLSAIh5WJvNJOkxBEr or -KWLSTak0H20X_2Qnanv" I mentioned above in my firebase database structure.
Here is what i was saying you to do:
Your model class:
class UsersII: NSObject {
var parentId: String?
var name: String?
var beschrijving: String?
var plaatje: String?
init(dictionary: [String: Any],parentId:String) {
self.name = dictionary["title"] as? String ?? ""
self.beschrijving = dictionary["beschrijving"] as? String ?? ""
self.plaatje = dictionary["image"] as? String ?? ""
self.parentId = parentId
}
}
Fetch user method:
func fetchUser() {
Database.database().reference().child("Blog").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = UsersII(dictionary: dictionary,parentId:snapshot.key)
self.users.append(user)
print(snapshot)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}, withCancel: nil)
}
And finaly you didSelect:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let message = users.reversed()[indexPath.row]
guard let beschrijving = message.beschrijving else {
return
}
guard let image = message.plaatje else {
return
}
guard let titel = message.name else {
return
}
guard let parentId = message.name else
{
return
}
UserDefaults.standard.set(beschrijving, forKey: "nieuwsBeschrijving")
UserDefaults.standard.set(image,forKey: "nieuwsPlaatje")
UserDefaults.standard.set(titel, forKey: "nieuwsTitel")
UserDefaults.standard.set(parentId,forKey: "nieuwsParentId")
self.performSegue(withIdentifier: "gotonews", sender: nil)
}
}

how to make checkmark to be selected depending on the array in swift 3?

I am having array in which selected name will be stored and passed to before view controller and when ever i need to go previous view controller then the previously selected check mark needs to be selected but here it is enabling the last selected element only the problem is if i select three then it is not selecting three it is check marking only the last element but i need the three selected can anyone help me how to make the check mark to be selected for three elements ?
protocol ArrayToPass: class {
func selectedArrayToPass(selectedStrings: [String])
}
class FilterSelectionViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
var productName = [String]()
var productprice = [String]()
var imageArray = [String]()
var idArray = [Int]()
let urlString = "http://www.json-generator.com/api/json/get/bOYOrkIOSq?indent=2"
var values = [String]()
var selected: Bool?
var delegate: ArrayToPass?
var nameSelection: Bool?
var namesArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.downloadJsonWithURL()
tableDetails.separatorInset = UIEdgeInsets.zero
activityIndicator.startAnimating()
tableDetails.isHidden = true
tableDetails.dataSource = self
tableDetails.delegate = self
let rightBarButton = UIBarButtonItem(title: "Apply", style: UIBarButtonItemStyle.plain, target: self, action: #selector(applyBarButtonActionTapped(_:)))
self.navigationItem.rightBarButtonItem = rightBarButton
tableDetails.estimatedRowHeight = UITableViewAutomaticDimension
tableDetails.rowHeight = 60
// Do any additional setup after loading the view.
}
func applyBarButtonActionTapped(_ sender:UIBarButtonItem!){
self.delegate?.selectedArrayToPass(selectedStrings: values)
navigationController?.popViewController(animated: true)
}
func downloadJsonWithURL() {
let url = NSURL(string: urlString)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSArray {
for item in jsonObj! {
if let itemDict = item as? NSDictionary{
if let name = itemDict.value(forKey: "name") {
self.productName.append(name as! String)
}
if let price = itemDict.value(forKey: "value") {
self.productprice.append(price as! String)
}
if let image = itemDict.value(forKey: "img") {
self.imageArray.append(image as! String)
}
if let id = itemDict.value(forKey: "id") {
self.idArray.append(id as! Int)
}
}
}
OperationQueue.main.addOperation({
self.tableDetails.reloadData()
})
}
}).resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productName.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "filterSelectionCell", for: indexPath) as! FilterSelectionCell
activityIndicator.stopAnimating()
activityIndicator.hidesWhenStopped = true
tableDetails.isHidden = false
cell.brandProductName.text = productName[indexPath.row]
if nameSelection == true{
if namesArray.count != 0 {
print(namesArray)
for name in namesArray{
if productName[indexPath.row].contains(name){
print(productName[indexPath.row])
cell.accessoryType = .checkmark
}
else {
cell.accessoryType = .none
}
}
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
selected = false
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
if cell.accessoryType == .checkmark{
cell.accessoryType = .none
print("\(productName[indexPath.row])")
values = values.filter{$0 != "\(productName[indexPath.row])"}
selected = true
}
else{
cell.accessoryType = .checkmark
}
}
if selected == true{
print(values)
}
else{
getAllTextFromTableView()
}
print(values)
}
func getAllTextFromTableView() {
guard let indexPaths = self.tableDetails.indexPathsForSelectedRows else { // if no selected cells just return
return
}
for indexPath in indexPaths {
values.append(productName[indexPath.row])
}
}
here is the image for this
Basically do not manipulate the view (the cell). Use a data model.
struct Product {
let name : String
let value : String
let img : String
let id : Int
var selected = false
init(dict : [String:Any]) {
self.name = dict["name"] as? String ?? ""
self.value = dict["value"] as? String ?? ""
self.img = dict["img"] as? String ?? ""
self.id = dict["id"] as? Int ?? 0
}
}
And never use multiple arrays as data source . That's a very bad habit.
Declare the data source array as
var products = [Product]()
Parse the JSON data and do a (better) error handling
func downloadJsonWithURL() {
let url = URL(string: urlString)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil { print(error!); return }
do {
if let jsonObj = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] {
self.products = jsonObj.map{ Product(dict: $0) }
DispatchQueue.main.async {
self.tableDetails.reloadData()
}
}
} catch {
print(error)
}
}
task.resume()
}
in cellForRow... assign the name to the label and set the checkmark depending on selected
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "filterSelectionCell", for: indexPath)
let product = products[indexPath.row]
cell.textLabel!.text = product.name
cell.accessoryType = product.selected ? .checkmark : .none
return cell
}
In didSelect... toggle selected and reload the row
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selected = products[indexPath.row].selected
products[indexPath.row].selected = !selected
tableView.reloadRows(at: [indexPath], with: .none)
}
To get all selected items is very easy, too.
let selectedItems = products.filter{ $0.selected }
or get only the names
let selectedNames = products.filter{ $0.selected }.map{ $0.name }
There is no need at all to get any information from the view. The controller gets the information always from the model and uses tableview data source and delegate to update the view.
If you want to pass data to another view controller pass Product instances. They contain all relevant information.

How to display a multidimensional array under different sections in swift 3

I have been trying to create multidimensional arrays and display each array under each section .. but I keep ending up with a fatal error index out of range . The code that i display below is how I access my firebase data and print each array from the database.. issue is I get the data I assign an array to it create it but can’t seem to display it due to the error . Hope someone helps for reference regarding the error I have attached a screenshot as well.
#IBOutlet weak var tableview: UITableView!
var yourArray = [String]()
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var sundaycoursenamearray = [String]()
var sundaycoursecodearray = [String]()
var sundaycourselocationarray = [String]()
var sundayfromarray = [String]()
var sundaytoarray = [String]()
var mondaycoursenamearray = [String]()
var mondaycoursecodearray = [String]()
var mondaycourselocationarray = [String]()
var mondayfromarray = [String]()
var mondaytoarray = [String]()
var tuesdaycoursenamearray = [String]()
var tuesdaycoursecodearray = [String]()
var tuesdaycourselocationarray = [String]()
var tuesdayfromarray = [String]()
var tuesdaytoarray = [String]()
var wednesdaycoursenamearray = [String]()
var wednesdaycoursecodearray = [String]()
var wednesdaycourselocationarray = [String]()
var wednesdayfromarray = [String]()
var wednesdaytoarray = [String]()
var thursdaycoursenamearray = [String]()
var thursdaycoursecodearray = [String]()
var thursdaycourselocationarray = [String]()
var thursdayfromarray = [String]()
var thursdaytoarray = [String]()
var fridaycoursenamearray = [String]()
var fridaycoursecodearray = [String]()
var fridaycourselocationarray = [String]()
var fridayfromarray = [String]()
var fridaytoarray = [String]()
var saturdaycoursenamearray = [String]()
var saturdaycoursecodearray = [String]()
var saturdaycourselocationarray = [String]()
var saturdayfromarray = [String]()
var saturdaytoarray = [String]()
var coursecodes = [[String]]()
var coursenames = [[String]]()
var courselocations = [[String]]()
var fromtimes = [[String]]()
var totimes = [[String]]()
var days = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
override func viewWillAppear(_ animated: Bool) {
let uid = Auth.auth().currentUser?.uid
if(uid == nil){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nextviewController = storyboard.instantiateViewController(withIdentifier: "loginscreen")
//self.present(profileViewController, animated: true, completion: nil)
self.present(nextviewController, animated: true, completion: nil)
}
for j in days {
for i in 1 ..< 10 {
let ref1 = Database.database().reference().child("users").child((uid)!).child("courses").child(j).child(String(i))
ref1.observeSingleEvent(of: .value, with: { snapshot in
if let dictionary = snapshot.value as? [String: AnyObject] {
// print(dictionary)
if j == "Sunday" {
if let points = dictionary["coursname"] as? String {
self.sundaycoursecodearray.append(points)
print(self.sundaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.sundaycoursenamearray.append(points1)
print(self.sundaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.sundaycourselocationarray.append(points1)
print(self.sundaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.sundayfromarray.append(points1)
print(self.sundayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.sundaytoarray.append(points1)
print(self.sundaytoarray)
}
}
if j == "Monday" {
if let points = dictionary["coursname"] as? String {
self.mondaycoursecodearray.append(points)
print(self.mondaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.mondaycoursenamearray.append(points1)
print(self.mondaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.mondaycourselocationarray.append(points1)
print(self.mondaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.mondayfromarray.append(points1)
print(self.mondayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.mondaytoarray.append(points1)
print(self.mondaytoarray)
}
}
if j == "Tuesday" {
if let points = dictionary["coursname"] as? String {
self.tuesdaycoursecodearray.append(points)
print(self.tuesdaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.tuesdaycoursenamearray.append(points1)
print(self.tuesdaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.tuesdaycourselocationarray.append(points1)
print(self.tuesdaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.tuesdayfromarray.append(points1)
print(self.tuesdayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.tuesdaytoarray.append(points1)
print(self.tuesdaytoarray)
}
}
if j == "Wednesday" {
if let points = dictionary["coursname"] as? String {
self.wednesdaycoursecodearray.append(points)
print(self.wednesdaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.wednesdaycoursenamearray.append(points1)
print(self.wednesdaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.wednesdaycourselocationarray.append(points1)
print(self.wednesdaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.wednesdayfromarray.append(points1)
print(self.wednesdayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.wednesdaytoarray.append(points1)
print(self.wednesdaytoarray)
}
}
if j == "Thursday" {
if let points = dictionary["coursname"] as? String {
self.thursdaycoursecodearray.append(points)
print(self.thursdaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.thursdaycoursenamearray.append(points1)
print(self.thursdaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.thursdaycourselocationarray.append(points1)
print(self.thursdaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.thursdayfromarray.append(points1)
print(self.thursdayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.thursdaytoarray.append(points1)
print(self.thursdaytoarray)
}
}
if j == "Friday" {
if let points = dictionary["coursname"] as? String {
self.fridaycoursecodearray.append(points)
print(self.fridaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.fridaycoursenamearray.append(points1)
print(self.fridaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.fridaycourselocationarray.append(points1)
print(self.fridaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.fridayfromarray.append(points1)
print(self.fridayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.fridaytoarray.append(points1)
print(self.fridaytoarray)
}
}
if j == "Saturday" {
if let points = dictionary["coursname"] as? String {
self.saturdaycoursecodearray.append(points)
print(self.saturdaycoursecodearray)
}
if let points1 = dictionary["coursecode"] as? String {
self.saturdaycoursenamearray.append(points1)
print(self.saturdaycoursenamearray)
}
if let points1 = dictionary["courseroomlocation"] as? String {
self.saturdaycourselocationarray.append(points1)
print(self.saturdaycourselocationarray)
}
if let points1 = dictionary["fromtime"] as? String {
self.saturdayfromarray.append(points1)
print(self.saturdayfromarray)
}
if let points1 = dictionary["totime"] as? String {
self.saturdaytoarray.append(points1)
print(self.saturdaytoarray)
}
}
self.coursecodes.append(self.sundaycoursenamearray)
self.coursecodes.append(self.mondaycoursenamearray)
self.coursecodes.append(self.tuesdaycoursenamearray)
self.coursecodes.append(self.wednesdaycoursenamearray)
self.coursecodes.append(self.thursdaycoursenamearray)
self.coursecodes.append(self.fridaycoursenamearray)
self.coursecodes.append(self.saturdaycoursenamearray)
self.coursenames.append(self.sundaycoursecodearray)
self.coursenames.append(self.mondaycoursecodearray)
self.coursenames.append(self.tuesdaycoursecodearray)
self.coursenames.append(self.wednesdaycoursecodearray)
self.coursenames.append(self.thursdaycoursecodearray)
self.coursenames.append(self.fridaycoursecodearray)
self.coursenames.append(self.saturdaycoursecodearray)
self.courselocations.append(self.sundaycourselocationarray)
self.courselocations.append(self.mondaycourselocationarray)
self.courselocations.append(self.tuesdaycourselocationarray)
self.courselocations.append(self.wednesdaycourselocationarray)
self.courselocations.append(self.thursdaycourselocationarray)
self.courselocations.append(self.fridaycourselocationarray)
self.courselocations.append(self.saturdaycourselocationarray)
self.fromtimes.append(self.sundayfromarray)
self.fromtimes.append(self.mondayfromarray)
self.fromtimes.append(self.tuesdayfromarray)
self.fromtimes.append(self.wednesdayfromarray)
self.fromtimes.append(self.thursdayfromarray)
self.fromtimes.append(self.fridayfromarray)
self.fromtimes.append(self.saturdayfromarray)
self.totimes.append(self.sundaytoarray)
self.totimes.append(self.mondaytoarray)
self.totimes.append(self.tuesdaytoarray)
self.totimes.append(self.wednesdaytoarray)
self.totimes.append(self.thursdaytoarray)
self.totimes.append(self.fridaytoarray)
self.totimes.append(self.saturdaytoarray)
self.tableview.reloadData()
}
})
} }
super.viewWillAppear(animated)
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return days[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return days.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return coursenames[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! homeTableViewCell
// Configure the cell...
// cell.classcode?.text = sundaycoursenamearray[indexPath.section]
cell.classcode?.text = coursenames[indexPath.section][indexPath.row]
return cell
}
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
You should create a model for your data then create arrays that include the model.
You can create a simple struct like so:
struct Course {
var name : String?
var code: String?
var location: String?
var toTime: String?
var fromTime: String?
}
After you create the model you can create an array that contains this type of struct.
Also for numberOfRowsInSection you don't have to use subscript. Just return:
return coursenames.count
That is why you get the error.

Making table view section expand swift

I am following this tutorial for expanding and collapsing my table view section. As this demo is done in swift 2.2 I have made all the changes according to swift 3.0 . I am stuck at the below function at if condition(currentSectionCells[row]["isVisible"]) which gives me error as "Type 'NSFastEnumerationIterator.Element' (aka 'Any' has no subscript members)'".
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0...((currentSectionCells as! [[String: AnyObject]]).count - 1) {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
I have tried type casting it as below
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0...((((currentSectionCells) as? NSMutableArray)?.count)! - 1) {
let temp = [currentSectionCells][row] as? NSMutableDictionary
let temp2 = temp?["isVisible"] as! Bool
if temp2 == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
But this gives me a crash at runtime on this line "let temp2 = temp?["isVisible"] as! Bool"
Crash says "EXC_BAD_INSTRUCTION" and the temp shows as nil.
Please help guys. TIA
Table View Delegate and Data Source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if cellDescriptors != nil {
return cellDescriptors.count
}
else {
return 0
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return visibleRowsPerSection[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath: indexPath as NSIndexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! CustomCell
if currentCellDescriptor["cellIdentifier"] as! String == "sectionCellIdentifier" {
if let primaryTitle = currentCellDescriptor["secondaryTitle"]
{
cell.sectionTitleLabel.text = primaryTitle as? String
}
}
else if currentCellDescriptor["cellIdentifier"] as! String == "shortAnswerCell" {
cell.questionTitle.text = currentCellDescriptor["primaryTitle"] as? String
cell.questionTextView.text = currentCellDescriptor["secondaryTitle"] as? String
cell.answerTextView.text = currentCellDescriptor["answerTitle"] as? String
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let temp = cellDescriptors[indexPath.section] as? NSArray
let temp2 = temp?[indexOfTappedRow ] as? NSDictionary
let temp3 = temp2?["isExpandable"] as! Bool
if temp3 == true {
var shouldExpandAndShowSubRows = false
if temp3 == false {
// In this case the cell should expand.
shouldExpandAndShowSubRows = true
}
temp2?.setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (temp2?["additionalRows"] as! Int)) {
(temp![i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
getIndicesOfVisibleRows()
tblExpandable.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: UITableViewRowAnimation.fade)
}
I worked on that tutorial as well and completed it successfully in swift3.Your solution is given below modify accordingly.
class yourClass: UIViewController
{
#IBOutlet weak var profileTableView: UITableView!
internal var visibleRowsPerSection = [[Int]]()
internal var cellDescriptors: NSMutableArray!
// VIEW DID LOAD
override func viewDidLoad() {
super.viewDidLoad()
profileTableView.showsVerticalScrollIndicator = false
loadProfileControllerData()
profileTableSetUp()
// Do any additional setup after loading the view.
}
func loadProfileControllerData(){
if let path = Bundle.main.path(forResource: "CellDescriptor", ofType: "plist") {
cellDescriptors = NSMutableArray(contentsOfFile: path)
}
getIndicesOfVisibleRows()
profileTableView.reloadData()
}
// SHOW PARENT VISIBLE ROWS AND SAVE THERE ROW INDEX IN ARRAY
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors.objectEnumerator().allObjects as! [[[String:Any]]]{
var visibleRows = [Int]()
for row in 0..<currentSectionCells.count {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
print(visibleRowsPerSection)
}
}
// GET REQUIRED OBJECT OF TYPE [String: Any]
func getCellDescriptorForIndexPath(indexPath: NSIndexPath) -> [String: Any] {
let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let cellDescriptorss = cellDescriptors[indexPath.section] as! NSArray
let data = cellDescriptorss.object(at: indexOfVisibleRow) as! [String:Any]
return data
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//----------------------
// EXTENSION TO OUR PROFILE CLASS THAT DETERMINE OUR CLASS CONFIRM 2 IMPORTANT DELEGATES
extension profileViewController : UITableViewDelegate,UITableViewDataSource{
//MARK-: TABLE VIEW DELEGATE FUNCTIONS
// RETURN NUMBER OF SECTION IN TABLE VIEW
public func numberOfSections(in tableView: UITableView) -> Int{
if cellDescriptors.count != 0{
return cellDescriptors.count
}
else{
return 0
}
}
// RETURN NUMBER OF ROWS IN EACH SECTION OF TABLE VIEWS
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return visibleRowsPerSection[section].count
}
/* Return object of UITableViewCell that contains table SECTON data and USER profile data */
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath: indexPath as NSIndexPath)
let menuCell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! yourCellClass
if currentCellDescriptor["cellIdentifier"] as! String == "parent"{
}
else if currentCellDescriptor["cellIdentifier"] as! String == "child"{
menuCell.backgroundColor = UIColor.clear
}
return menuCell
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let cellDescriptorss = cellDescriptors[indexPath.section] as! NSArray
var data = cellDescriptorss.object(at: indexOfTappedRow) as! [String:Any]
if data["isExpandable"] as! Bool == true{
var shouldExpandAndShowSubRows = false
if data["isExpanded"] as! Bool == true{
shouldExpandAndShowSubRows = false
(cellDescriptorss[indexOfTappedRow] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
}
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (data["additionalRows"] as! Int)) {
(cellDescriptorss[i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
getIndicesOfVisibleRows()
self.profileTableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: UITableViewRowAnimation.fade)
}
Thank You for helping me out, I was stuck at a point where the sections weren't expanding even after your help, so just made some changes in the syntax as Swift 3.0 is very specific about type casting hence the didSelectRowAt wasn't functioning properly. Here is the complete didSelectRowAt method. Happy coding.
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if (cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow] ["isExpandable"] as! Bool == true {
var shouldExpandAndShowSubRows = false
if (cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow]["isExpanded"] as! Bool == false {
// In this case the cell should expand.
shouldExpandAndShowSubRows = true
}
((cellDescriptors[indexPath.section] as! NSMutableArray)[indexOfTappedRow] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + ((cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow]["additionalRows"] as! Int)) {
((cellDescriptors[indexPath.section] as! NSMutableArray)[i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
Swift 3/4 without use of NSMutable arrays based on the tutorial and all the code wrapped in a model.
class CellsDescriptorModel {
private var cellDescriptors: [[[String:Any]]]!
private var visibleRowsPerSection : [[Int]]
var CellDescriptors : [[[String:Any]]] { get { return cellDescriptors }}
var VisibleRowsPerSection : [[Int]] { get { return visibleRowsPerSection }}
init(plist:String) {
visibleRowsPerSection = [[Int]]()
if let url = Bundle.main.url(forResource:plist, withExtension: "plist") {
do {
let data = try Data(contentsOf:url)
cellDescriptors = try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as! [[[String:Any]]]
} catch {
print(error)
}
}
getIndicesOfVisibleRows()
}
func getCellDescriptorForIndexPath(indexPath: IndexPath) -> [String: Any] {
let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row]
return cellDescriptors[indexPath.section][indexOfVisibleRow]
}
func expandCell(indexPath:IndexPath) {
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if cellDescriptors[indexPath.section][indexOfTappedRow] ["isExpandable"] as! Bool == true {
var shouldExpandAndShowSubRows = false
if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] as! Bool == false {
shouldExpandAndShowSubRows = true
}
cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] = shouldExpandAndShowSubRows
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (cellDescriptors[indexPath.section][indexOfTappedRow]["additionalRows"] as! Int)) {
cellDescriptors[indexPath.section][i]["isVisible"] = shouldExpandAndShowSubRows
}
}
else {
if cellDescriptors[indexPath.section][indexOfTappedRow]["cellIdentifier"] as! String == "DataPickerTableViewCell" {
var indexOfParentCell: Int!
for index in (0..<indexOfTappedRow).reversed() {
if cellDescriptors[indexPath.section][index]["isExpandable"] as! Bool == true {
indexOfParentCell = index
break
}
}
cellDescriptors[indexPath.section][indexOfParentCell]["secondaryTitle"] = ""
cellDescriptors[indexPath.section][indexOfParentCell]["isExpanded"] = false
for i in (indexOfParentCell + 1)...(indexOfParentCell + (cellDescriptors[indexPath.section][indexOfParentCell]["additionalRows"] as! Int)) {
cellDescriptors[indexPath.section][i]["isVisible"] = false
}
}
}
getIndicesOfVisibleRows()
}
private func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0..<currentSectionCells.count {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
}

Resources