Saturday, 2 November 2019

TableView Cell Expand and collapse with out any library in Swift


JsonFile (parse json data)

 [

  {
  "name": "rings",
  "sub_category": [
                   {
    
                   "name": "engagement",
                   "display_name": "Engagement"
                   },
                   {

                   "name": "band-couple band",
                   "display_name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed scelerisque aliquet arcu, sed placerat turpis. Sed vulputate finibus nisi, nec gravida turpis consectetur quis. Nullam quis vestibulum ex. Integer lacinia quam sed rutrum tempus. Sed quis metus mollis, euismod ipsum in, vulputate turpis. Fusce suscipit ligula in efficitur interdum. Sed ante velit, vulputate nec nibh commodo, tempus molestie eros. Etiam consequat enim nisi, a mollis nisi gravida et. Fusce scelerisque ex vitae turpis fermentum facilisis."
                   },
                   {

                   "name": "Navaratnam+Collection",
                   "display_name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed scelerisque aliquet arcu, sed placerat turpis."
                   },
                   {

                   "name": "cocktail",
                   "display_name": "Lorem ipsum dolor sit amet"
                   },
                   {

                   "name": "below+15000",
                   "display_name": "Under 15,000/-"
                   },
                   {

                   "name": "hearts",
                   "display_name": "Heart Rings"
                   },
                   {

                   "name": "halo",
                   "display_name": "Halo Rings"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "earrings",
  "sub_category": [
                   {

                   "name": "studs",
                   "display_name": "Studs"
                   },
                   {
                   "name": "drops",
                   "display_name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed scelerisque aliquet arcu, sed placerat turpis. Sed vulputate finibus nisi, nec gravida turpis consectetur quis. Nullam quis vestibulum ex. Integer lacinia quam sed rutrum tempus. Sed quis metus mollis, euismod ipsum in, vulputate turpis. Fusce suscipit ligula in efficitur interdum. Sed ante velit, vulputate nec nibh commodo, tempus molestie eros. Etiam consequat enim nisi, a mollis nisi gravida et. Fusce scelerisque ex vitae turpis fermentum facilisis."
                   },
                   {

                   "name": "hoops",
                   "display_name": "Hoops"
                   },
                   {
                   "name": "jhumki",
                   "display_name": "Jhumkis"
                   },
                   {
                   "name": "sui dhaga",
                   "display_name": "Sui-dhaga"
                   },
                   {
                   "name": "pearl",
                   "display_name": "Pearl"
                   },
                   {
                   "name": "below 15000",
                   "display_name": "Less Than 15,000/-"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "pendants",
  "sub_category": [
                   {
                   "name": "alphabet",
                   "display_name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed scelerisque aliquet arcu, sed placerat turpis."
                   },
                   {
                   "name": "religious",
                   "display_name": "Religious"
                   },
                   {
                   "name": "hearts",
                   "display_name": "Hearts"
                   },
                   {
                   "name": "diamond and colored stones",
                   "display_name": "Gemstone Pendants"
                   },
                   {
                   "name": "single stone-only diamond",
                   "display_name": "Single Diamond Pendants"
                   },
                   {
                   "name": "floral",
                   "display_name": "Floral"
                   },
                   {
                   "name": "locket",
                   "display_name": "Lockets"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "bangles",
  "sub_category": [
                   {
                   "name": "round bangle",
                   "display_name": "Round"
                   },
                   {
                   "name": "oval bangle",
                   "display_name": "Oval"
                   },
                   {
                   "name": "eternity",
                   "display_name": "Eternity"
                   },
                   {
                   "name": "yellow gold",
                   "display_name": "Gold"
                   },
                   {
                   "name": "wedding",
                   "display_name": "Bridal"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "bracelets",
  "sub_category": [
                   {
                   "name": "yellow gold",
                   "display_name": "Gold"
                   },
                   {
                   "name": "only diamond",
                   "display_name": "Diamond"
                   },
                   {
                   "name": "floral",
                   "display_name": "Floral"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "nose pins",
  "sub_category": [
                   {
                   "name": "yellow gold",
                   "display_name": "Gold"
                   },
                   {
                   "name": "only diamond",
                   "display_name": "Diamond"
                   },
                   {
                   "name": "only coloured stone",
                   "display_name": "Gem Stone"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "necklaces",
  "sub_category": [
                   {
                   "name": "yellow gold",
                   "display_name": "Gold"
                   },
                   {
                   "name": "wedding",
                   "display_name": "Bridal"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "tanmaniya",
  "sub_category": [
                   {
                   "name": "only diamond",
                   "display_name": "Diamond"
                   },
                   {
                   "name": "ruby",
                   "display_name": "Ruby"
                   },
                   {
                   "name": "below 50000",
                   "display_name": "Under 50,000/-"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "only diamond",
  "sub_category": [
                   {
                   "name": "rings",
                   "display_name": "Rings"
                   },
                   {
                   "name": "earrings",
                   "display_name": "Earrings"
                   },
                   {
                   "name": "pendants",
                   "display_name": "Pendants"
                   },
                   {
                   "name": "bangles",
                   "display_name": "Bangles"
                   },
                   {
                   "name": "bracelets",
                   "display_name": "Bracelets"
                   },
                   {
                   "name": "necklaces",
                   "display_name": "Necklaces"
                   },
                   {
                   "name": "nose pins",
                   "display_name": "Nose Pins"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "plain gold jewellery",
  "sub_category": [
                   {
                   "name": "rings",
                   "display_name": "Rings"
                   },
                   {
                   "name": "earrings",
                   "display_name": "Earrings"
                   },
                   {
                   "name": "pendants",
                   "display_name": "Pendants"
                   },
                   {
                   "name": "bangles",
                   "display_name": "Bangles"
                   },
                   {
                   "name": "bracelets",
                   "display_name": "Bracelets"
                   },
                   {
                   "name": "necklaces",
                   "display_name": "Necklaces"
                   },
                   {
                   "name": "nose pins",
                   "display_name": "Nose Pins"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "diamond and colored stones",
  "sub_category": [
                   {
                   "name": "rings",
                   "display_name": "Rings"
                   },
                   {
                   "name": "earrings",
                   "display_name": "Earrings"
                   },
                   {
                   "name": "pendants",
                   "display_name": "Pendants"
                   },
                   {
                   "name": "bangles",
                   "display_name": "Bangles"
                   },
                   {
                   "name": "bracelets",
                   "display_name": "Bracelets"
                   },
                   {
                   "name": "necklaces",
                   "display_name": "Necklaces"
                   },
                   {
                   "name": "nosepins",
                   "display_name": "Nosepins"
                   },
                   {
                   "name": "tanmaniyas",
                   "display_name": "Tanmaniyas"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "platinum",
  "sub_category": [
                   {
                   "name": "single stone",
                   "display_name": "Single Stone"
                   },
                   {
                   "name": "multistone",
                   "display_name": "Multi Stone"
                   },
                   {
                   "name": "single stone",
                   "display_name": "Single Stone"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "men",
  "sub_category": [
                   {
                   "name": "rings",
                   "display_name": "Rings"
                   },
                   {
                   "name": "pendants",
                   "display_name": "Pendants"
                   },
                   {
                   "name": "bracelets",
                   "display_name": "Bracelets"
                   },
                   {
                   "name": "earrings",
                   "display_name": "Earrings"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  },
  {
  "name": "kids",
  "sub_category": [
                   {
                   "name": "earrings",
                   "display_name": "Earrings"
                   },
                   {
                   "name": "pendants",
                   "display_name": "Pendants"
                   },
                   {
                   "name": "view all",
                   "display_name": "View All"
                   }
                   ]
  }
  ]



DATA MODEL


//
//  Category.swift
//  Assignment
//
//  Created by vishnu duggisetty on 02/November/2019 .
//  Copyright © 2019 vishnu duggisetty. All rights reserved.
//

import Foundation

struct categoryDetails:Decodable {
    var name:String
    var sub_category:[subCategoryDetails]
}

struct subCategoryDetails:Decodable {
    var name:String
    var display_name:String
}





REQUEST

//
//  CategoryRequest.swift
//  Assignment
//
//  Created by vishnu duggisetty on 02/November/2019 .
//  Copyright © 2019 vishnu duggisetty. All rights reserved.
//

import Foundation

enum catError:Error{
    case NoData
    case ParseError
    case NoFilePath
}

struct CategoryRequest {
    func getCategories(completion:@escaping(Result<[categoryDetails], catError>) -> Void){
        if let filePath = Bundle.main.path(forResource: "category", ofType: "json"){
            do {
                 let jsonData = try Data(contentsOf: URL(fileURLWithPath: filePath), options: .mappedIfSafe)
                let decoder = JSONDecoder()
                let categoryResponse = try decoder.decode([categoryDetails].self, from: jsonData)
                print(categoryResponse)
                completion(.success(categoryResponse))
            } catch let error{
                print(error.localizedDescription)
                completion(.failure(.ParseError))
            }
        }
        else{
            completion(.failure(.NoFilePath))
        }
    }
}




//
//  CategoryTableViewController.swift
//  Assignment
//
//  Created by vishnu duggisetty on 02/November/2019 .
//  Copyright © 2019 vishnu duggisetty. All rights reserved.
//

import UIKit

class CategoryTableViewController: UITableViewController {

    var expandIndex: Int = -1
    var expandSectionHead: UITableViewHeaderFooterView!
    
    
    var listofCategories = [categoryDetails](){
        didSet{
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        self.getData()
        self.tableView.tableFooterView = UIView()
        // 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
    }

    func getData(){
        let cateRequest = CategoryRequest()
        cateRequest.getCategories{[weak self] result in
            switch result{
            case .failure(let error):
                print(error)
            case .success(let categoryList):
                self?.listofCategories = categoryList
            }
        }
    }
    
    
    
    // MARK: - Table view data source

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        if(self.expandIndex == section){
            return self.listofCategories[section].sub_category.count
        }
        else{
            return 0
        }
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryTableCell", for: indexPath)
        let name = self.listofCategories[indexPath.section].sub_category[indexPath.row].name
        let displayName = self.listofCategories[indexPath.section].sub_category[indexPath.row].display_name
        cell.textLabel?.attributedText = makeAttributedString(title: name, subtitle: displayName)
        cell.textLabel?.numberOfLines = 0;
        return cell
    }
}

//TableView Sections
extension CategoryTableViewController{
    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        if(self.listofCategories.count > 0){
            self.tableView.backgroundView = nil
            return self.listofCategories.count
        }
        
        return 0
    }
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        if(self.listofCategories.count > 0){
            return self.listofCategories[section].name.uppercased()
        }
        return ""
    }
    
    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 60
    }
    
    override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
        let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
        header.contentView.backgroundColor = UIColor.white
        header.textLabel?.textColor = UIColor.blue
        let separator = UIView(frame: CGRect(x: 0, y: header.frame.size.height, width: header.frame.size.width, height: 1))
        separator.backgroundColor = UIColor.lightGray
        header.addSubview(separator)

        header.tag = section
        let headerGesture = UITapGestureRecognizer()
        headerGesture.addTarget(self, action: #selector(self.sectionHeaderTap(_:)))
        header.addGestureRecognizer(headerGesture)
    }
}

//Expandable and collapsable methods
extension CategoryTableViewController{
    @objc func sectionHeaderTap(_ sender: UITapGestureRecognizer) {
        let headerView = sender.view as! UITableViewHeaderFooterView
        let section = headerView.tag
        //If no expanded cell, expandIndex will be -1
        if (self.expandIndex == -1) {
            self.expandIndex = section
            tableViewExpandSection(section)
        }
        else {
            //If same expand cell is selected, then collapse
            if (self.expandIndex == section) {
                tableViewCollapeSection(section)
            }
            else {
                //First collapse then expand
                tableViewCollapeSection(self.expandIndex)
                tableViewExpandSection(section)
            }
        }
    }
    
    func tableViewExpandSection(_ section: Int) {
        let subCat = self.listofCategories[section].sub_category
        if (subCat.count == 0) {
            self.expandIndex = -1;
            return;
        }
        else {
            var indexesPath = [IndexPath]()
            for i in 0 ..< subCat.count {
                let index = IndexPath(row: i, section: section)
                indexesPath.append(index)
            }
            self.expandIndex = section
            self.tableView!.beginUpdates()
                self.tableView!.insertRows(at: indexesPath, with: UITableView.RowAnimation.fade)
            self.tableView!.endUpdates()
        }
        
        //Scroll the tableview to selected cell
        self.tableView.scrollToRow(at: IndexPath(item: 0, section: section), at: .top, animated: true)
    }
    
    func tableViewCollapeSection(_ section: Int) {
        let subCat = self.listofCategories[section].sub_category
        //Make expandIndex to -1 when collapse is selected
        self.expandIndex = -1;
        if (subCat.count == 0) {
            return;
        }
        else {
            var indexesPath = [IndexPath]()
            for i in 0 ..< subCat.count {
                let index = IndexPath(row: i, section: section)
                indexesPath.append(index)
            }
            self.tableView!.beginUpdates()
                self.tableView!.deleteRows(at: indexesPath, with: UITableView.RowAnimation.fade)
            self.tableView!.endUpdates()
        }
    }
}

//Change the text format
extension CategoryTableViewController{
    func makeAttributedString(title: String, subtitle: String) -> NSAttributedString {
        let titleAttributes = [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline), NSAttributedString.Key.foregroundColor: UIColor.purple]
        let subtitleAttributes = [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .subheadline)]

        let titleString = NSMutableAttributedString(string: "\(title)\n", attributes: titleAttributes)
        let subtitleString = NSAttributedString(string: subtitle, attributes: subtitleAttributes)

        titleString.append(subtitleString)

        return titleString
    }
}







Setting Up Multiple App Targets in Xcode from a Single Codebase

 To create two different apps (like "Light" and "Regular") from the same codebase in Xcode, you can follow these steps b...