I have 6 custom cells in the table view. In that 3 of them have an image view I added tapGesture to imageView and enabled the user interaction of imageView still not working. I also tried to add tapGesture in main tread in table view cell class but no use, I even tried to add gesture in the cell for at IndexPath method but not worked, how to solve this?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellArr = sections[indexPath.section].cell
//Loading cell based on array details
switch cellArr[0] {
case is InsuranceDetails:
let insuranceCell = tableView.dequeueReusableCell(withIdentifier: "insuranceCell") as! InsuranceCell
insuranceCell.setup(object: cellArr[indexPath.row], in: tableView, at: indexPath)
case is Pollution:
let pollutionCell = tableView.dequeueReusableCell(withIdentifier: "pollutionCell") as! PollutionCell
pollutionCell.setup(object: cellArr[indexPath.row], in: tableView, at: indexPath)
return pollutionCell
case is Servicing:
let servicingCell = tableView.dequeueReusableCell(withIdentifier: "servicingCell") as! ServicingCell
servicingCell.setup(object: cellArr[indexPath.row], in: tableView, at: indexPath)
return servicingCell
case is ChallanPaid:
let challanPaidCell = tableView.dequeueReusableCell(withIdentifier: "challanPaidCell") as! ChallanPaidCell
challanPaidCell.setup(object: cellArr[indexPath.row], in: tableView, at: indexPath)
return challanPaidCell
case is InsuranceClaims:
let insuranceClaimCell = tableView.dequeueReusableCell(withIdentifier: "insuranceClaimCell") as! InsuranceClaimCell
insuranceClaimCell.setup(object: cellArr[indexPath.row], in: tableView, at: indexPath)
return insuranceClaimCell
case is FuelRefills:
let fuelRefillCell = tableView.dequeueReusableCell(withIdentifier: "fuelRefillCell") as! FuelRefillCell
fuelRefillCell.setup(object: cellArr[indexPath.row], in: tableView, at: indexPath)
return fuelRefillCell
default:
return UITableViewCell()
}
}
one of my tableview cell class
class InsuranceCell: UITableViewCell {
#IBOutlet weak var insurancePhoto: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setup(object: NSManagedObject, in table: UITableView, at indexPath: IndexPath) {
guard let arr = object as? InsuranceDetails else {return}
let tapgesture = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
insurancePhoto.isUserInteractionEnabled = true
tapgesture.numberOfTapsRequired = 1
insurancePhoto.isUserInteractionEnabled = true
insurancePhoto.addGestureRecognizer(tapgesture)
// Do time consuming stuff in the background
DispatchQueue.global(qos: .userInitiated).async {
let imageData = arr.insurancePhoto ?? Data()
let image = UIImage(data: imageData)
let compimage = image?.resized(withPercentage: 0.5)
// Always back to the main thread/queue for UI updates
DispatchQueue.main.async {
guard let cell = table.cellForRow(at: indexPath) as? InsuranceCell else { return }
cell.insurancePhoto.image = compimage
}
}
}
}
Related
I already known how to make a basic tableView, below is my basic code
class ShoppingTableViewController: UITableViewController{
var description = ["D1", "299900", "D2", "P201712310000003000", "D3", "ASS+DfDFxSuu", "D10", "901", "D11", "00,46246226301561000110001001", "D12", "20201231123030"]
var dictDescription = ["D10": "901", "D11": "00,46246226301561000110001001", "D3": "ASS+DfDFxSuu", "D12": "20201231123030", "D2": "P201712310000003000", "D1": "299900"]
override func viewDidLoad() {
super.viewDidLoad()
// Xib
tableView.register(UINib(nibName:PropertyKeys.pictureCell , bundle: nil), forCellReuseIdentifier: PropertyKeys.pictureCell)
tableView.register(UINib(nibName:PropertyKeys.infoCell , bundle: nil), forCellReuseIdentifier: PropertyKeys.infoCell)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.pictureCell, for: indexPath) as! PictureWithTableViewCell
cell.iconImageView.image = UIImage(named: "amount")
cell.lbDescription.text = "Type"
cell.lbName.text = "Shopping"
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.infoCell, for: indexPath) as! InfoTableViewCell
cell.lbDescription.text = "Taiwan dollar"
cell.lbName.text = m_dictDescription["D1"]
return cell
case 2:
let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.pictureCell, for: indexPath) as! PictureWithTableViewCell
cell.iconImageView.image = UIImage(named: "info")
cell.lbDescription.text = "BankName"
cell.lbName.text = m_dictDescription["D11"]
return cell
case 3:
if m_dictDescription["D2"] != nil {
let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.infoCell, for: indexPath) as! InfoTableViewCell
cell.lbDescription.text = "orderNumber"
cell.lbName.text = m_dictDescription["D2"]
return cell
}else {
let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.infoCell, for: indexPath) as! InfoTableViewCell
cell.isHidden = true
return cell
}
but this is an unsafe way because i write func number of rows and cell for row as hardcode.
so I want to change tableView composing way from decide cell format first then fill data in (like my basic code) to let data decide my number of rows and cell for row. (use indexPath)
but I got some problems:
I try to write:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let test = self.m_arrDescription[indexPath.row]
cell.lbName.text = test
It works but every cell looks the same while I want to present different cells.
I search some information on internet, perhaps I can use struct and combine m_arrDescription to make tableview cell.
// use struct to decide cell label or image , for example: cell.lbDescription.text ...
struct CellFormat {
var title : String
var image : UIImage
var name : String
}
So far this is what i've written, can anyone please kindly help me to go on?
Be clearly, how do I use [indexPath.row] in this code ?
please create first one enum
enum CellType: String, CaseIterable {
case title, image, name
}
and then use this code in tableview delegate methods.
switch CellType(rawValue: indexPath.row) {
case .title:
break
case .image:
break
case .name:
break
case .none:
break
}
If any problem let me know
I have a view with an embedded UITableViewController that is filled with custom cells. When I hit the save button I would like the getProjectName() within the UITableViewController to return the projectNameTF data within in the custom cell. Currently when I try to get the cell within the getProjectName() it returns a nil cell.
Main View:
class NewProjectView: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func saveBtn(_ sender: UIButton) {
print("Save button hit")
print(NewProjectTableViewController().getProjectName())
}
}
Embedded TableViewController
import UIKit
struct cellType{
var mainTitle = String()
var numOfChildCells = Int()
var opened = Bool()
}
class NewProjectTableViewController: UITableViewController {
var tableViewData = [cellType]()
var customCellData = [UITableViewCell]()
var projectNameTFR = UITextField()
// Counts the number of cells and displays them
override func numberOfSections(in tableView: UITableView) -> Int {
return tableViewData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// If the parent cell is opened display the number of cells inside it
if tableViewData[section].opened == true {
return tableViewData[section].numOfChildCells + 1
}
else {
return 1
}
}
//
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
self.tableView.separatorStyle = .none
// Do this for the header cell
if indexPath.row == 0{
guard let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell") as? HeaderCell else {return UITableViewCell()}
cell.backgroundColor = .clear
cell.setUpCell(title: tableViewData[indexPath.section].mainTitle)
// If cell should be opened, display correct open image
if tableViewData[indexPath.section].opened{
cell.openCell()
}
// else display closed image
else{
cell.closeCell()
}
return cell
// else it is a child cell
}else {
switch tableViewData[indexPath.section].mainTitle{
// Load Project info cell
case "Project Information":
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ProjectNameCell") as? ProjectNameCell else {return UITableViewCell()}
projectNameTFR = cell.projectNameTF
return cell
case "Client Information":
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ClientInfoCell") as? ClientInfoCell else {return UITableViewCell()}
return cell
default:
print("defaulted cell")
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") else {return UITableViewCell()}
return cell
}
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableViewData[indexPath.section].opened == true{
tableViewData[indexPath.section].opened = false
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none)
let headerCell = tableView.cellForRow(at: indexPath) as! HeaderCell
}
else{
tableViewData[indexPath.section].opened = true
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none)
let headerCell = tableView.cellForRow(at: indexPath) as! HeaderCell
}
}
override func viewDidLoad() {
super.viewDidLoad()
//self.definesPresentationContext = true
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = UITableView.automaticDimension
// Do any additional setup after loading the view.
print("add new client screen loaded")
registerTableViewCells()
// Create the cells
tableViewData = [cellType(mainTitle: "Project Information", numOfChildCells: 1, opened: true ),
cellType(mainTitle: "Client Information", numOfChildCells: 1, opened: false )]
}
override func viewWillAppear(_ animated: Bool) {
// Add a background view to the table view
let backgroundImage = UIImage(named: "App Background.png")
let imageView = UIImageView(image: backgroundImage)
self.tableView.backgroundView = imageView
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
private func registerTableViewCells(){
let ClientInfoCell = UINib(nibName: "ClientInfoCell", bundle: nil)
self.tableView.register(ClientInfoCell, forCellReuseIdentifier: "ClientInfoCell")
let ProjectNameCell = UINib(nibName: "ProjectNameCell", bundle: nil)
self.tableView.register(ProjectNameCell, forCellReuseIdentifier: "ProjectNameCell")
let HeaderCell = UINib(nibName: "HeaderCell", bundle: nil)
self.tableView.register(HeaderCell, forCellReuseIdentifier: "HeaderCell")
let SaveCell = UINib(nibName: "SaveCell", bundle: nil)
self.tableView.register(SaveCell, forCellReuseIdentifier: "SaveCell")
}
func getProjectName() -> String{
let indexPath = NSIndexPath(row: 0, section: 0)
let cell = tableView?.cellForRow(at: indexPath as IndexPath) as? ProjectNameCell
print(type(of: cell))
if(cell==nil){
print("cell is nil")
}
return "I returned this test string"
}
}
Custom Cell I am trying to reach
import UIKit
class ProjectNameCell: UITableViewCell {
#IBOutlet weak var projectNameTF: UITextField!
var projectName = String()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
backgroundColor = .clear
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Well sir you are getting cell on index path 0 section number and 0th row
let indexPath = NSIndexPath(row: 0, section: 0)
on that index you have HeaderCell instead of ProjectNameCell thats why you are getting nil
this line can't cast your HeaderCell to ProjectNameCell
let cell = tableView?.cellForRow(at: indexPath as IndexPath) as? ProjectNameCell
My model:
enum NHAnswerType:Int
{
case NHAnswerCheckboxButton = 1
case NHAnswerRadioButton = 2
case NHAnswerSmileyButton = 3
case NHAnswerStarRatingButton = 4
case NHAnswerTextButton = 5
}
class NH_QuestionListModel: NSObject {
var dataListArray33:[NH_OptionsModel] = []
var id:Int!
var question:String!
var buttontype:String!
var options:[String]?
var v:String?
var answerType:NHAnswerType?
var optionsModelArray:[NH_OptionsModel] = []
init(dictionary :JSONDictionary) {
guard let question = dictionary["question"] as? String,
let typebutton = dictionary["button_type"] as? String,
let id = dictionary["id"] as? Int
else {
return
}
// (myString as NSString).integerValue
self.answerType = NHAnswerType(rawValue: Int(typebutton)!)
print(self.answerType?.rawValue)
if let options = dictionary["options"] as? [String]{
print(options)
print(options)
for values in options{
print(values)
let optionmodel = NH_OptionsModel(values: values)
self.optionsModelArray.append(optionmodel)
}
}
self.buttontype = typebutton
self.question = question
self.id = id
}
}
In viewcontroller:
override func viewDidLoad() {
super.viewDidLoad()
self.tableview?.register(NH_SmileyCell.nib, forCellReuseIdentifier: NH_SmileyCell.identifier)
self.tableview?.register(NH_StarRatingCell.nib, forCellReuseIdentifier: NH_StarRatingCell.identifier)
self.tableview?.register(NH_CheckBoxCell.nib, forCellReuseIdentifier: NH_CheckBoxCell.identifier)
self.tableview?.register(NH_RadioTypeCell.nib, forCellReuseIdentifier: NH_RadioTypeCell.identifier)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let model = questionViewModel.titleForHeaderInSection(atsection: indexPath.row)
// print(model.answerType?.hashValue)
print(model.answerType)
print(model.answerType?.rawValue)
switch model.answerType {
case .NHAnswerRadioButton?:
if let cell = tableview.dequeueReusableCell(withIdentifier: NH_RadioTypeCell.identifier, for: indexPath) as? NH_RadioTypeCell {
cell.textLabel?.text = "dfdsfgs"
// cell.item = item
return cell
}
case .NHAnswerCheckboxButton?:
if let cell = tableView.dequeueReusableCell(withIdentifier: NH_CheckBoxCell.identifier, for: indexPath) as? NH_CheckBoxCell {
cell.textLabel?.text = "dfdsfgs"
// cell.item = item
return cell
}
case .NHAnswerSmileyButton?:
if let cell = tableView.dequeueReusableCell(withIdentifier: NH_SmileyCell.identifier, for: indexPath) as? NH_SmileyCell {
cell.textLabel?.text = "dfdsfgs"
// cell.item = item
return cell
}
case .NHAnswerStarRatingButton?:
if let cell = tableView.dequeueReusableCell(withIdentifier: NH_StarRatingCell.identifier, for: indexPath) as? NH_StarRatingCell {
cell.textLabel?.text = "dfdsfgs"
// cell.item = item
return cell
}
// case .NHAnswerTextButton?:
// if let cell = tableView.dequeueReusableCell(withIdentifier: NH_TextCell.identifier, for: indexPath) as? NH_TextCell{
// // cell.item = item
// return cell
// }
default:
return UITableViewCell()
}
And in the radiotypecell:
class NH_RadioTypeCell: UITableViewCell {
#IBOutlet weak var questionLabel: UILabel?
var item: NH_QuestionViewModelItem? {
didSet {
guard let item = item as? NHQuestionViewModelQuestionNameItem else {
return
}
questionLabel?.text = item.question
}
}
static var nib:UINib {
return UINib(nibName: identifier, bundle: nil)
}
static var identifier: String {
return String(describing: self)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
This always produces the following error message:
NH_CheckBoxCell - must register a nib or a class for the identifier or
connect a prototype cell in a storyboard'
How can I fix the problem?
What changes should I do in while registration of the cell in the tableview in swift?
I have stuck on this issue for 2 days. I don't know why.
I have a collection view for shopping cart purpose. there is a stepper on the reusable cell with a valueChanged event.
I use the following code to handle the problem:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! CartCollectionViewCell
cell.name?.text = cart[indexPath.row].value[0]
let url = URL(string: (baseUrl + (cart[indexPath.row].value[1])))
cell.img.setImageWith(url!)
cell.price?.text = "$" + (cart[indexPath.row].value[2])
cell.remove.addTarget(self, action: #selector(cartViewController.remove_from_cart(sender:)), for: .touchDown)
cell.remove.tag = indexPath.row
cell.qty.value = Double(cart[indexPath.row].value[3])!
cell.qty.addTarget(self, action: #selector(cartViewController.stepperValueChanged(stepper:)), for: .valueChanged)
cell.qty.tag = indexPath.row
return cell
}
func stepperValueChanged(stepper: GMStepper) {
let pos = stepper.convert(CGPoint.zero, to: collection)
let indexPath = collection.indexPathForItem(at: pos)!
let cell: CartCollectionViewCell = collection.cellForItem(at: indexPath) as! CartCollectionViewCell
if cell.qty != nil {
let value = String(Int(cell.qty.value))
var item = cart[indexPath.row]
item.value[3] = value
cart[indexPath.row] = item
}
}
func remove_from_cart(sender: UIButton){
let pos = sender.convert(CGPoint.zero, to: collection)
let indexPath = collection.indexPathForItem(at: pos)!
cart.remove(at: indexPath.row)
amount = 0.0
collection.reloadData()
}
after I scroll the collection view following error occurs:
"fatal error: unexpectedly found nil while unwrapping an Optional value"
let cell: CartCollectionViewCell = collection.cellForItem(at: indexPath) as! CartCollectionViewCell
this line is highlighted by Xcode.
I have a UITableView and inside the tableViewCell I have a UICollectionView.
My requirement is while tapping on a button of first tableView cell I have to animate second tableViewCell.
Below is my code :-
//Cell For Row at indexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (0 == indexPath.section) {
let cell = tableView.dequeueReusableCell(withIdentifier: "FirstRowCell") as! FirstRowCell
cell.btnReview.addTarget(self, action: #selector(GotoReview), for: UIControlEvents.touchUpInside)
cell.btnMyProduct.addTarget(self, action: #selector(AnimateCollectionView), for: UIControlEvents.touchUpInside)
//cell.
return cell
} else if ( 1 == indexPath.section) {
let identifier = "TableCollectionCell"
var tableCollectionCell = tableView.dequeueReusableCell(withIdentifier: identifier) as? TableCollectionCell
if(tableCollectionCell == nil) {
let nib:Array = Bundle.main.loadNibNamed("TableCollectionCell", owner: self, options: nil)!
tableCollectionCell = nib[0] as? TableCollectionCell
tableCollectionCell?.delegate = self
}
return tableCollectionCell!
} else {
let identifier = "BrandImagesTableCell"
var brandImagesTableCell = tableView.dequeueReusableCell(withIdentifier: identifier)
if(brandImagesTableCell == nil) {
let nib:Array = Bundle.main.loadNibNamed("BrandImagesTableCell", owner: self, options: nil)!
brandImagesTableCell = nib[0] as? BrandImagesTableCell
}
//brandImagesTableCell.
return brandImagesTableCell!
}
}
In my code you can see:
if (0 == indexPath.section)
In that I have a button target (#selector(AnimateCollectionView)).
I want to animate tableCollectionCell which is at (1 == indexPath.section).
See my AnimateCollectionView method :-
func AnimateCollectionView() {
let identifier = "TableCollectionCell"
var tableCollectionCell = tableView.dequeueReusableCell(withIdentifier: identifier) as? TableCollectionCell
if(tableCollectionCell == nil) {
let nib:Array = Bundle.main.loadNibNamed("TableCollectionCell", owner: self, options: nil)!
tableCollectionCell = nib[0] as? TableCollectionCell
tableCollectionCell?.delegate = self
}
tableCollectionCell?.alpha = 0
UIView.animate(withDuration: 1.50, animations: {
//self.view.layoutIfNeeded()
tableCollectionCell?.alpha = 1
})
}
If you want to animate its change, you can just change some state variables, call tableView.reloadRows(at:with:), and have your cellForRowAt then check those state variables to know what the final cell should look like (i.e. whether it is the "before" or "after" cell configuration).
For example, here is an example, with two cells, toggling the second cell from red to blue cells.
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "RedCell", bundle: nil), forCellReuseIdentifier: "RedCell")
tableView.register(UINib(nibName: "BlueCell", bundle: nil), forCellReuseIdentifier: "BlueCell")
}
#IBAction func didTapButton(_ sender: UIButton) {
isRedCell = !isRedCell
tableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .fade)
}
var isRedCell = true
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "ButtonCell", for: indexPath)
return cell
} else if indexPath.row == 1 {
if isRedCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RedCell", for: indexPath)
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "BlueCell", for: indexPath)
return cell
}
}
fatalError("There are only two rows in this demo")
}
}
Now, assuming you really needed to use NIBs rather than cell prototypes, I would register the NIBs with the tableview like above, rather than having cellForRow have to manually instantiate them itself. Likewise, for my cell with the button, I just used a prototype cell in my storyboard and hooked the button directly to my #IBOutlet, avoiding a NIB for that cell entirely.
But all of that is unrelated to your main problem at hand: You can create your cells however you want. But hopefully this illustrates the basic idea, that on the tap of the button, I'm not trying to load any cells directly, but I just update some status variable that cellForRow will use to know which cell to load and then tell the tableView to animate the reloading of that IndexPath.
By the way, I assumed from your example that the two different potential cells for the second row required different NIBs. But if you didn't, it's even easier (use just one NIB and one cell identifier), but the idea is the same, just update your state variable and reload the second row with animation.