I am showing data like Expendable Tableview - ios

I am showing data like expendable tableview with a header section. Header is showing fine. but When I am showing rows . (Like in a product 5 addons then data is showing like in expendable Tableview Every Row with a Same Type Name Header). I am attaching screenshot. What type of data is showing.
Image Data Showing Like : https://imgur.com/a/JXqUbJD
I am attaching I current code which I am using . I already tried too many times but it is not working fine.
import UIKit
import Reachability
import Alamofire
class ExpendableTableview: UIViewController, UITableViewDelegate, UITableViewDataSource, CollapsibleTableViewHeaderDelegate {
#IBOutlet weak var tableview: UITableView!
var catid:Int!
var reachability = Reachability()!
var arraySubCategory = [structSubCategory]()
struct structSubCategory {
var id:Int
var minimum_people:String
var title:String
var package_price:String
var package_image:String
var package_label:String
var categoryId:Int
// var addons: [cateringAddOns]
// var collapsed: Bool
}
var arraySection = [section]()
struct section
{
var title: String
var addons : [cateringAddOns]
var collapsed: Bool
}
var arrayCateringAddOns = [cateringAddOns]()
struct cateringAddOns {
var no_of_items:Int
var add_on_type:Int
var package_item:String
var upgrade_price:String
}
var NormalArr = ["Balance", "Sales", "Order Status", "Social Media Complaints", "Logout"]
override func viewDidLoad() {
super.viewDidLoad()
print(catid)
SubCateringAPI(cateringId: catid)
// Do any additional setup after loading the view.
}
func numberOfSections(in tableView: UITableView) -> Int {
return arraySection.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arraySection[section].collapsed ? 0 : arraySection[section].addons.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellRID") as! ExpendableAddOnsCell
let item : cateringAddOns = arraySection[indexPath.section].addons[indexPath.row]
cell.lblAddon.text = item.package_item
return cell
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header") as? CollapsibleTableViewHeader ?? CollapsibleTableViewHeader(reuseIdentifier: "header")
header.titleLabel.text = arraySection[section].title
header.arrowLabel.text = ">"
header.setCollapsed(arraySection[section].collapsed)
header.section = section
header.delegate = self
return header
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44.0
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 1.0
}
func toggleSection(_ header: CollapsibleTableViewHeader, section: Int) {
let collapsed = !arraySection[section].collapsed
// Toggle collapse
arraySection[section].collapsed = collapsed
header.setCollapsed(collapsed)
tableview.reloadSections(NSIndexSet(index: section) as IndexSet, with: .automatic)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
// func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// return arraySection[section].title
// }
//
#IBAction func btnDismiss(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
func SubCateringAPI(cateringId:Int)
{
if (reachability.connection == .wifi) || (reachability.connection == .cellular)
{
SwiftLoader.show(animated: true)
let url = BaseUrl + ViewController.sharedInstance.subCatering + "cateringId=\(cateringId)"
print(url)
Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default).responseJSON { response in
switch response.result {
case .success:
let json = response.result.value
print(json)
let code = (json as AnyObject).object(forKey: "code") as! Int
print(code)
if code == 200
{
let data = (json as AnyObject).object(forKey: "data") as? NSArray
for alldata in data!
{
let id = (alldata as AnyObject).object(forKey: "id") as! Int
let minimum_people = (alldata as AnyObject).object(forKey: "minimum_people") as! String
let title = (alldata as AnyObject).object(forKey: "title") as! String
let package_price = (alldata as AnyObject).object(forKey: "package_price") as! String
let package_image = (alldata as AnyObject).object(forKey: "package_image") as! String
let package_label = (alldata as AnyObject).object(forKey: "package_label") as! String
let categoryId = (alldata as AnyObject).object(forKey: "categoryId") as! Int
let arr = structSubCategory(id: id, minimum_people: minimum_people, title: title, package_price: package_price, package_image: package_image, package_label: package_label, categoryId: categoryId)
self.arraySubCategory.append(arr)
}
for i in self.arraySubCategory
{
let id = i.categoryId
let title = i.package_label
self.cateringAddOndsAPI(adonsId: id, title: title)
}
SwiftLoader.hide()
}
else
{
}
case .failure:
print("error")
}
}
}
else
{
alert(title: "", message: "Please Check Your Internet Connection")
}
}
func cateringAddOndsAPI(adonsId:Int, title: String)
{
if (reachability.connection == .wifi) || (reachability.connection == .cellular)
{
arraySection.removeAll()
arrayCateringAddOns.removeAll()
SwiftLoader.show(animated: true)
let url = BaseUrl + ViewController.sharedInstance.cateringAddOnds + "adonsId=\(adonsId)"
print(url)
Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default).responseJSON { response in
switch response.result {
case .success:
let json = response.result.value
print(json)
let code = (json as AnyObject).object(forKey: "code") as! Int
print(code)
if code == 200
{
let data = (json as AnyObject).object(forKey: "data") as? NSArray
for alldata in data!
{
let no_of_items = (alldata as AnyObject).object(forKey: "no_of_items") as! Int
let add_on_type = (alldata as AnyObject).object(forKey: "add_on_type") as! Int
let package_item = (alldata as AnyObject).object(forKey: "package_item") as! String
let upgrade_price = (alldata as AnyObject).object(forKey: "upgrade_price") as! String
let arr = cateringAddOns(no_of_items: no_of_items, add_on_type: add_on_type, package_item: package_item, upgrade_price: upgrade_price)
self.arrayCateringAddOns.append(arr)
let expandData = section(title: title, addons: [arr], collapsed: false)
self.arraySection.append(expandData)
}
self.tableview.reloadData()
SwiftLoader.hide()
}
else
{
}
case .failure:
print("error")
}
}
}
else
{
alert(title: "", message: "Please Check Your Internet Connection")
}
}
}

you should call override func prepareForReuse() inside the class CollapsibleTableViewHeader.
override func prepareForReuse() {
super.prepareForReuse()
//TODO: set default values
}
Could you provide code of CollapsibleTableViewHeader class?

Related

How do I perform a clean JSON decoding with URL parameters and UITableview

I'm trying to create a function that parse my JSON according to the ID at the end of the URL. For example: (https://alodjinha.herokuapp.com/produto?categoriaId=1). In this case, "categoriaId=1" will return me a "Games" category as a JSON filled with Games. It should changes depending on each category the user clicks on my UICollectionView categories. So, if the user clicks in "Movies" on my UICollectionView, I gotta change the url to id 2 (for example) https://alodjinha.herokuapp.com/produto?categoriaId=2 then I'll get the JSON filled with Movies and so on. However, It's not working what I'm doing wrong?
That's how I'm trying to get the category ID:
func getCategoriaPorID(IdCategoria:Int, completion:#escaping ([CategoriaIDItems])->Void) {
let url = URL(string: "https://alodjinha.herokuapp.com/produto?categoriaId=\(IdCategoria)")
let session = URLSession.shared
let request = URLRequest(url: url!)
let dataTask = session.dataTask(with: request) { (data, response, error) in
guard let unwrappedData = data else { print("Error data"); return}
do {
let jsonTop10 = try JSONDecoder().decode(CategoriaIDItemsData.self, from: unwrappedData)
completion(jsonTop10.data)
}catch{
print("Could no get API data")
}
}
dataTask.resume()
}
Models:
import Foundation
//Categorias
struct Contents : Decodable {
let data : [Content]
}
struct Content : Decodable {
let id : Int
let descricao : String
let urlImagem : String
}
//Banner
struct BannerData : Decodable {
let data : [Banner]
}
struct Banner : Decodable {
let id : Int
let urlImagem : String
let linkUrl : String
}
//Top10
struct Top10Data:Decodable {
let data: [Top10]
}
struct Top10:Decodable {
let id : Int
let nome : String
let urlImagem : String
let descricao : String
let precoDe : Int
}
struct CategoriaIDItemsData:Decodable {
let data : [CategoriaIDItems]
}
struct CategoriaIDItems:Decodable {
let id : Int
let nome : String
let urlImagem : String
let descricao : String
let precoDe : Int
}
Well, after that I proceeded to the main file (ViewController) where contains all my tables, such as, UITableView and also UICollectionview (Where all the categories are located).
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource, UITableViewDataSource, UITableViewDelegate, UICollectionViewDelegate {
#IBOutlet weak var tableViewTopSell: UITableView!
#IBOutlet var collectionView: UICollectionView!
#IBOutlet weak var collectionViewBanner: UICollectionView!
var dataSource: [Content] = [Content]()
var dataBanner: [Banner] = [Banner]()
var dataTopSold: [Top10] = [Top10]()
var dataCategoriaID: [CategoriaIDItems] = [CategoriaIDItems]()
override func viewDidLoad() {
super.viewDidLoad()
//Delegate TableView
self.tableViewTopSell.delegate = self
//SetupNavBarCustom
self.navigationController?.navigationBar.CustomNavigationBar()
let logo = UIImage(named: "tag.png")
let imageView = UIImageView(image:logo)
self.navigationItem.titleView = imageView
//CallAPIData
getTopSold { (data) in
DispatchQueue.main.async {
self.dataTopSold = data
self.tableViewTopSell.reloadData()
}
}
getBanner { (data) in
DispatchQueue.main.async {
self.dataBanner = data
self.collectionViewBanner.reloadData()
}
}
getAudiobooksAPI { (data) in
DispatchQueue.main.async {
self.dataSource = data
self.collectionView.reloadData()
}
}
}
//CollectionView
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (collectionView == self.collectionView) {
return self.dataSource.count
}else{
return self.dataBanner.count
}}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (collectionView == self.collectionView) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! CollectionViewCell
let content = self.dataSource[indexPath.item]
cell.bookLabel.text = content.descricao
cell.bookImage.setImage(url: content.urlImagem, placeholder: "")
return cell
}else if (collectionView == self.collectionViewBanner) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCellBanner", for: indexPath) as! CollectionViewCell
let content = self.dataBanner[indexPath.item]
cell.bannerImage.setImage(url: content.urlImagem, placeholder: "")
return cell
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
var indexPathId: Int
if (collectionView == self.collectionView) {
let content = self.dataSource[indexPath.item]
indexPathId = content.id
}else if (collectionView == self.collectionViewBanner) {
let content = self.dataBanner[indexPath.item]
indexPathId = content.id
}
getCategoriaPorID(IdCategoria: indexPathId) { (data) in
self.dataCategoriaID = data
self.performSegue(withIdentifier: "segueCategorias", sender:self.dataCategoriaID)
print(self.dataCategoriaID)
}
}
//TableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataTopSold.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "topSoldCell", for: indexPath) as! TableViewCell
let content = self.dataTopSold[indexPath.row]
cell.labelNomeTopSell.text = content.nome
cell.imageViewTopSell.setImage(url: content.urlImagem, placeholder: "")
cell.labelPrecoDe.text = "R$ \(content.precoDe)"
//Colocar strike em cima do Preco Antigo
let oldPrice = "R$ \(content.precoDe)"
let promotionString = oldPrice + ""
let attributedStr = NSMutableAttributedString(string: promotionString)
let crossAttr = [NSAttributedStringKey.strikethroughStyle: NSUnderlineStyle.styleSingle.rawValue]
attributedStr.addAttributes(crossAttr, range: NSMakeRange(0, oldPrice.count))
cell.labelPrecoDe.attributedText = attributedStr
//
cell.labelPrecoPor.text = "R$ 119.99"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "segueId", sender:self.dataTopSold[indexPath.row])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueId" {
let des = segue.destination as? TelaDetalheProdutos
//.item possui uma propriedade instanciada na TelaDetalheProdutos
des?.item = (sender as? Top10)
//Segue para CollectionView Categorias
} else if segue.identifier == "segueCategorias" {
let desc = segue.destination as? TelaCategorias
desc?.item = (sender as? CategoriaIDItems)
}
}
}
//Cast UIImage Extension
extension UIImageView{
func setImage(url : String, placeholder: String, callback : (() -> Void)? = nil){
self.image = UIImage(named: "no-photo")
URLSession.shared.dataTask(with: NSURL(string: url)! as URL, completionHandler: { (data, response, error) -> Void in
guard error == nil else{
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
self.image = image
if let callback = callback{
callback()
}
})
}).resume()
}
}
Screen that will receive the data:
import UIKit
class TelaCategorias: UIViewController, UITableViewDataSource, UITableViewDelegate {
//Class Instanciated
var item:CategoriaIDItems?
var nome = String()
override func viewDidLoad() {
super.viewDidLoad()
????
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ???
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableIDCategorias", for: indexPath) as! TelaCategoriasCell
????
return cell
}
}
App picture:
Main Screen
QUESTION SOLVED SUCCESSFULLY.
FIXED

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)
}
}

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)
}
}
}

TableView With Sections Template

I followed this Tutorial
And it works fine. Now if I try to take the sections data from a JSON this also works fine, but I have problems displaying everything.
This is what i did:
// TestVC.swift
// WinterdienstTest
//
// Created by zwipf on 09.02.17.
// Copyright © 2017 Squirrel. All rights reserved.
//
import UIKit
class TestVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
let sections: [String] = ["Tour 1", "Tour 2", "Tour 3"]
var adressTour1: [String] = []
var adressTour2: [String] = []
var adressTour3: [String] = []
var adressTourALL: [Int: [String]] = [:]
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// URL abrufen
let url = NSURL(string: "https://api.myjson.com/bins/mml65")
let request = NSMutableURLRequest(url: url! as URL)
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
// JSON parsen und Ergebins in eine Liste von assoziativen Arrays wandeln
let responseString = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
// Daten auslesen
if let Liegenschaften = responseString["Liegenschaften"] as? [AnyObject] {
for liegenschaft in Liegenschaften {
if let Winterdienst = liegenschaft["Winterdienst"] as? [AnyObject] {
for winterdienst in Winterdienst {
let tour = winterdienst["Tour"] as! String
if tour == "1" {
let adresse = liegenschaft["Adresse"] as! String
self.adressTour1.append(adresse)
}
if tour == "2" {
let adresse = liegenschaft["Adresse"] as! String
self.adressTour2.append(adresse)
}
if tour == "3" {
let adresse = liegenschaft["Adresse"] as! String
self.adressTour3.append(adresse)
}
}
}
}
}
self.adressTourALL = [0:self.adressTour1, 1:self.adressTour2, 2:self.adressTour3]
}
// UI-Darstellung aktualisieren
//OperationQueue.main.addOperation {
// self.tableView.reloadData()
//}
// task.resume()
print(self.adressTourALL)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return adressTourALL.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell");
}
print(adressTourALL[indexPath.section]![indexPath.row])
cell!.textLabel!.text = adressTourALL[indexPath.section]![indexPath.row]
return cell!
}
}
Hope somebody can help me!
Among the others, you need to put this inside your code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section{
case 0:
return self.adressTour1.count
case 1:
return self.adressTour2.count
case 2:
return self.adressTour2.count
default:
return 1
}

How do I extract a variable from a UITable DidSelectAtRow?

I have an instance where a user picks from a UITable. The selected record has a name and an id associated with it.
At the moment to verify the name and id are being correctly reported I am using
let tempCountryId = (self.newCountries[cellCountryId!])
print (tempCountryId)
Country(name: Optional("England"), countryId: Optional("5"))
I want to be able to store that countryId in a variable so I can repopulate my UITable with data (Football Divisions) that match the countryId '5'
How do I do this?
This is my full script:
import UIKit
class PickTeamViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var teamsTableView: UITableView!
var pickedCountryID: Int?
var selectedCellCountryTitle: String?
var cellCountryId: Int?
struct Country {
var name: String?
var countryId: String?
init(_ dictionary: [String : String]) {
self.name = dictionary["name"]
self.countryId = dictionary["id"]
}
}
struct Divisions {
var divisionName: String?
var divisionId: String?
init(_ dictionary: [String : String]) {
self.divisionName = dictionary["name"]
self.divisionId = dictionary["country_id"]
}
}
struct Teams {
var teamName: String?
var newTeamId: String?
init(_ dictionary: [String : String]) {
self.teamName = dictionary["name"]
}
}
struct TeamId {
var newTeamId: String?
init(_ dictionary: [String : String]) {
self.newTeamId = dictionary["id"]
}
}
var newCountries = [Country]()
var newDivisions = [Divisions]()
var newTeams = [Teams]()
var newTeamId = [TeamId]()
override func viewDidAppear(_ animated: Bool) {
let myUrl = URL(string: "http://www.quasisquest.uk/KeepScore/getTeams.php?");
var request = URLRequest(url:myUrl!);
request.httpMethod = "GET";
let task = URLSession.shared.dataTask(with: myUrl!) { (data: Data?, response: URLResponse?, error: Error?) in
DispatchQueue.main.async
{
if error != nil {
print("error=\(error)")
return
}
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any]
print (json)
if let arr = json?["countries"] as? [[String:String]] {
self.newCountries = arr.flatMap { Country($0) }
self.teamsTableView.reloadData()
}
if let arr = json?["divisions"] as? [[String:String]] {
self.newDivisions = arr.flatMap { Divisions($0) }
}
if let arr = json?["teams"] as? [[String:String]] {
self.newTeams = arr.flatMap { Teams($0) }
}
self.teamsTableView.reloadData()
} catch{
print(error)
}
}
}
task.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.newCountries.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let country = newCountries[indexPath.row]
let cell = UITableViewCell()
cell.textLabel?.text = country.name
cell.textLabel?.font = UIFont(name: "Avenir", size: 12)
cell.textLabel?.textColor = UIColor.black
cell.backgroundColor = UIColor.white
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
cellCountryId = indexPath.row
// print (self.newCountries[cellCountryId!])
let tempCountryId = (self.newCountries[cellCountryId!])
print (tempCountryId)
}
override func viewDidLoad() {
super.viewDidLoad()
self.teamsTableView.delegate = self
self.teamsTableView.dataSource = self
// Do any additional setup after loading the view.
}
}
As discussed in the comments you should use another view controller to show the details. In didSelectRowAtIndexPath method take out the selected country from newCountries array and pass it to the DetailViewController.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let countryDetailsVC = self.storyboard?.instantiateViewController(withIdentifier: "CountryDetailsViewController") as! DetailViewController
countryDetailsVC.country = selectedCountry
present(countryDetailsVC, animated: true, completion: nil)
}
Now that you have the country Struct you can show its details in the DetailViewController.
Your table is populated from the array newCountries. So, to replace the contents of the table, you would need to replace the contents of newCountries and reload the table.
But that is not a very wise strategy. It would be better to show a different view controller with a different table and a different data array.

Resources