Table view cell elements not able to click and get data - ios

I have one table view and inside that i placed one main view. And inside that main view i placed one button.And when ever use click on my cell button. I need to get the cell title label.This is what i need. But i tried following below code. Not sure what i am missing out. It not at all calling my cell.add target line.
Code in cell for row at index:
cell.cellBtn.tag = indexPath.row
cell.cellBtn.addTarget(self, action:#selector(self.buttonPressed(_:)), for:.touchUpInside)
#objc func buttonPressed(_ sender: AnyObject) {
print("cell tap")
let button = sender as? UIButton
let cell = button?.superview?.superview as? UITableViewCell
let indexPath = tableView.indexPath(for: cell!)
let currentCell = tableView.cellForRow(at: indexPath!)! as! KMTrainingTableViewCell
print(indexPath?.row)
print(currentCell.cellTitleLabel.text)
}
I even added a breakpoint, still it not at calling my cell.addTarget line
Tried with closure too. In cell for row at index:
cell.tapCallback = {
print(indexPath.row)
}
In my table view cell:
var tapCallback: (() -> Void)?
#IBAction func CellBtndidTap(_ sender: Any) {
print("Right button is tapped")
tapCallback?()
}
Here that print statement is getting print in console.

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var list = [String]()
#IBOutlet weak var tableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
cell.saveButton.tag = indexPath.row
//cell.saveButton.accessibilityIdentifier = "some unique identifier"
cell.tapCallback = { tag in
print(tag)
}
return cell
}
}
class MyTableViewCell: UITableViewCell {
// MARK: - IBOutlets
#IBOutlet weak var saveButton: UIButton!
// MARK: - IBActions
#IBAction func saveTapped(_ sender: UIButton) {
tapCallback?(sender.tag)
}
// MARK: - Actions
var tapCallback: ((Int) -> Void)?
}

Actually this is not a good programming practice to add the button (which contains in table view cell) target action in view controller. We should follow the protocol oriented approach for it. Please try to under stand the concept.
/*This is my cell Delegate*/
protocol InfoCellDelegate {
func showItem(item:String)
}
/*This is my cell class*/
class InfoCell: UITableViewCell {
//make weak reference to avoid the Retain Cycle
fileprivate weak var delegate: InfoCellDelegate?
//Outlet for views
#IBOutlet var showButton: UIButton?
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
//This is the public binding function which will bind the data & delegate to cell
func bind(with: DataModel?, delegate: InfoCellDelegate?, indexPath: IndexPath) {
//Now the bind the cell with data here
//.....
//Assign the delegate
self.delegate = delegate
}
//Button action
#IBAction func rowSelected(sender: UIButton) {
self.delegate?.showItem(item: "This is coming from cell")
}
}
/*Now in your ViewController you need to just confirm the InfoCellDelegate & call the bind function*/
class ListViewController: UIViewController {
//Views initialisation & other initial process
}
//Table view Delegate & Data source
extension ListViewController: UITableViewDataSource, UITableViewDelegate {
/**
Configure the table views
*/
func configureTable() {
//for item table
self.listTable.register(UINib.init(nibName: "\(InfoCell.classForCoder())", bundle: nil), forCellReuseIdentifier: "\(InfoCell.classForCoder())")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "InfoCell") as! InfoCell
cell.bind(with: DataModel, delegate: self, indexPath: indexPath)
return cell
}
}
extension ListViewController: InfoCellDelegate {
func showItem(item) {
print(item)
}
}

Related

How to use one tableview's button to automatically select the other tableview's button all

I'm making a tableview that allows users to select items, primary is the main item, and each primary item contains a secondary list, I created a custom cell and also inserted the other tableview inside, as shown in the picture link below.
Form picture
Here's my ViewController.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, PrimaryDelegate {
#IBOutlet weak var tableView: UITableView!
//This is my data
var cardList = Card(userName: "Kevin", primaryList: [Card.Primary(primaryName: "Card1", secondaryList: [Card.Primary.Secondary(secondaryName: "Card1-1")]),Card.Primary(primaryName: "Card2", secondaryList: [Card.Primary.Secondary(secondaryName: "Card2-1"),Card.Primary.Secondary(secondaryName: "Card2-2")])])
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.reloadData()
}
#IBAction func enterAction(_ sender: Any) {
//I hope here can print the result
//How should I get the result from primaryList and secondaryList in Custom Cell ?
print(cardList)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cardList.primaryList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
cell.primaryLabel.text = cardList.primaryList[indexPath.row].primaryName
cell.secondaryList = cardList.primaryList[indexPath.row].secondaryList
cell.primaryIndex = indexPath.row
cell.primaryDelegate = self
return cell
}
func primaryIndex(index:Int) {
//I use delegate to get index, but how to tell which secondaryList needs to be selected all?
print("primaryIndex\(index)")
}
}
Here's my Custom Cell, it includes the other tableview, that's why I feel complicated.
class CustomTableViewCell: UITableViewCell,UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var primaryBtn: UIButton!
#IBOutlet weak var secondaryBtn: UIButton!
#IBOutlet weak var primaryLabel: UILabel!
#IBOutlet weak var secondaryLabel: UILabel!
#IBOutlet weak var secondaryTableView: UITableView!
#IBOutlet weak var secondaryHeight: NSLayoutConstraint!
var primaryIndex:Int?
var primaryDelegate:PrimaryDelegate?
var secondaryList:[Card.Primary.Secondary]!{
didSet{
secondaryTableView.delegate = self
secondaryTableView.dataSource = self
secondaryTableView.reloadData()
secondaryHeight.constant = secondaryTableView.contentSize.height
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return secondaryList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
cell.secondaryLabel.text = secondaryList[indexPath.row].secondaryName
return cell
}
#IBAction func primaryBtnAction(_ sender: UIButton) {
sender.isSelected = !primaryBtn.isSelected
primaryDelegate?.primaryIndex(index: primaryIndex!)
}
#IBAction func secondaryBtnAction(_ sender: UIButton) {
sender.isSelected = !secondaryBtn.isSelected
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
I hope it can be...
1.When the user selects the primary item, it can automatically help me select all the secondary items. However, the primary item can only be selected "one", and when the user selects the next primary item, the previous one needs to be canceled including all secondary items.
2.When the user press enterAction, it can print the data that user have chosen.I need to know what if user not choose primary, How many secondary list's item is selected.
I mean like result is Card1-1 and Card2-1, they only choose item in secondary list.
How should I tell custom cell's tableview to select all when I choose primary item, and how did custom cell know which primary is selected and needs to reload Data?
If need further information, please let me know, this selection rule make me really confused.Thanks
You can use this at your primaryBtnAction :
if sender.isSelected{
for section in 0..<tableView.numberOfSections {
for row in 0..<tableView.numberOfRows(inSection: section) {
tableView.selectRow(at: IndexPath(row: row, section: section), animated: false, scrollPosition: .none)
}
}
} else { //Deselect statement
tableView.deselectRow(at: IndexPath(row: row, section: section), animated: false)
}
Hope it helps...

Segue from TableView into another Tableview using gesture

I have FirstTableView (TableView) which is populated by cells that have an image and label. If users tap the label, I want to segue into another tableview (SecondTableView), which will use the label from the previous tableview as a path(foldername) to search for images within that folder and display them as cells.
I have bits and pieces of the code written together, but I'm not quite sure where to pass the perform segue and prepare for segue functions.
Here is the extension of my FirstTableViewController, which populate the cells of the FirstTableView:
FirstTableView
extension FirstTableViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataCells.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let folderCell = dataCells[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "FirstTableCell") as! FirstTableViewCell
cell.setCell(cell:folderCell)
return cell
}
}
Here is the code for the FirstTAbleViewCell. Notice that I've created the UITapGestureRecognizer constant and a protocol. If the FolderLabel is clicked, I want to segue into the next tableview.
func setCell is just the function to populate the FirstTableViews.
FirstTableViewCells
import UIKit
protocol LabelCellDelegate {
func func1(sender:Any)
}
class FirstTableViewCell: UITableViewCell {
#IBOutlet weak var ImageView: UIImageView!
#IBOutlet weak var FolderLabel: UILabel!
var delegate: LabelCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
FolderLabel.isUserInteractionEnabled = true
let recognizer = UITapGestureRecognizer(target: self, action:Selector(("handleTap:")))
recognizer.numberOfTapsRequired = 1
self.FolderLabel.addGestureRecognizer(recognizer)
}
func handleTap(recognizer: UITapGestureRecognizer){
delegate!.func1(sender: self)
}
func setCell (cell : FolderCell){
self.ImageView.image = cell.image
self.FolderLabel.text = cell.label
}
}
Next is the SecondTableViewController.
Somehow, after the tap of the cell from the FirstTableView I want to get the label name that was tapped, and use that name as a directory to search.
Grab all the images within the directory and display them as images in cells in SecondTableView. I'm not quite sure where I should be calling the gesture function and which TableViewController should be the delegate of the cell protocol.
SecondTableView
import UIKit
class SecondTableViewController: UIViewController {
var Imagefiles: [String] //Imagefiles String. This are all images from tapped folder label.
var CellObjects: [folderCell] // Cellobjects to fill the cells. contains images and image names.
// need to get folder name from FirstTableViewCell how?
override func viewDidLoad() {
super.viewDidLoad()
imageFiles = getImageObjects(folder: Foldercell) //how to get access to folderCell???????
cellObjects = createArray(imageFiles: imageFiles)
}
func getImageObjects(folder: FolderCell) -> imageFiles: String {
//FUNCTION To search tapped label and grab all image file paths.
func createArray(imageFiles: string)-> tempArray: [folderCell]{
//We use this function to take the image files from above function and
//create cell objects to populate our cells
//in SecondTableView
}
}
}
}
extension ViewController: LabelCellDelegate{
func1(folderLabel : String) {
return folderLabel
}
}
extension LablesTableView:UITableViewDataSource,UITableViewDelegate{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return CellObjects.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellFolderObject = folderObjects[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "SecondCell") as! LabelCell
let folderCell = CellObjects[indexPath.row]
cell.setCell(cell: folderCell)
return cell
As you can see, I haven't called perform segue and prepare for segue. I'm not sure where I should be calling them.
I'm also unsure about where the delegate to the gesture protocol should be.
Thank you.
Inside cellForRowAt set
cell.delegate = self
//
then inside this delegate function of the VC ( conform to the protocol LabelCellDelegate )
extension FirstTableViewController:LabelCellDelegate {
func func1(sender: FirstTableViewCell) {
self.performSegue(withIdentifier: "goToSecond", sender:sender.FolderLabel.text!)
}
}
//
the VC method is triggered from here
func handleTap(recognizer: UITapGestureRecognizer){
delegate!.func1(sender: self)
}
//
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondController = segue.destination as? SecondTableViewController {
secondController.sendedStr = sender as! String
}
}

I was wondering how to make a cell go to another view controller in Xcode 9, swift

I've been trying to figure out how to configure a cell to go to another view, in this case, I'm listing a group of services after login and when the user taps on a service they like, it takes them to a map. But I don't know how to set the cell up in a way that it takes them to the map when its tapped. I've tried creating a segue but nothing happens when the cell is tapped. I'm new to programming and was wondering if someone could explain this.
I've watched a bunch of youtube videos which gave me the understanding on how to set up the cell (basic stuff).
Would really appreciate some advice, thanks!
Hope this post helps anyone that's dipping their feet into the programming journey!
Thank you, happy coding!
Here is the code I currently have:
import UIKit
struct cellData {
let cell : Int!
let text : String!
let image : UIImage! }
class ListServicesTVC: UITableViewController {
var arrayOfCellData = [cellData]()
override func viewDidLoad() {
arrayOfCellData = [cellData(cell : 1, text : "Barber Services", image : #imageLiteral(resourceName: "barberservice") ),
cellData(cell : 2, text : "Salon Services", image : #imageLiteral(resourceName: "salonservice"))]
}
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].cell == 1 {
let cell = Bundle.main.loadNibNamed("BarberServiceCell", owner: self, options: nil)?.first as! BarberServiceCell
cell.barberImageView.image = arrayOfCellData[indexPath.row].image
cell.barberServicesLabel.text = arrayOfCellData[indexPath.row].text
return cell
}
else if arrayOfCellData[indexPath.row].cell == 2 {
let cell = Bundle.main.loadNibNamed("SalonServicesCell", owner: self, options: nil)?.first as! SalonServicesCell
cell.salonImageView.image = arrayOfCellData[indexPath.row].image
cell.salonServicesLabel.text = arrayOfCellData[indexPath.row].text
return cell
}
else {
let cell = Bundle.main.loadNibNamed("BarberServiceCell", owner: self, options: nil)?.first as! BarberServiceCell
cell.barberImageView.image = arrayOfCellData[indexPath.row].image
cell.barberServicesLabel.text = arrayOfCellData[indexPath.row].text
return cell
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if arrayOfCellData[indexPath.row].cell == 1 {
return 120
}
else if arrayOfCellData[indexPath.row].cell == 2 {
return 120
}
else {
return 120
}
}
}
Just follow the steps below:
create A tableView Outlet in ViewController Class.
create a TableViewCell Class and register with tableView Outlet.
then, create a DetailViewController Class ( i.e, When You click on a particular cell, it should show details of that particular cell)
In main "ViewController" do the following code
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {'
#IBOutlet var tableView: UITableView!
var tableData: [String] = ["Apple", "Samsung", "LG"]
// 1
override func viewDidLoad() {
super.viewDidLoad()
// Register customCell with tableView Outlet
let nib = UINib(nibName: "CustomTableViewCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
}
// 2
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableData.count
}
// 3
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CustomTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! CustomTableViewCell
// injecting data to cell
cell.lblCompanyName.text = tableData[indexPath.row]
cell.imgCompanyName.image = UIImage(named: tableData[indexPath.row])
return cell
}
// 4
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let detailObj=DetailViewController(nibName: "DetailViewController", bundle: nil)
self.navigationController?.pushViewController(detailObj, animated: true)
detailObj.nameVar=tableData[indexPath.row]
detailObj.imgStr=tableData[indexPath.row]
}
In "CustomTableViewCell" class
class CustomTableViewCell: UITableViewCell {
#IBOutlet var imgCompanyName: UIImageView!
#IBOutlet var lblCompanyName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}}
in "DetailViewController"
class DetailViewController: UIViewController {
#IBOutlet var name: UILabel!
#IBOutlet var image: UIImageView!
var nameVar:String?
var imgStr:String?
override func viewDidLoad() {
name.text=nameVar
image.image=UIImage(named: imgStr!)
super.viewDidLoad()
// Do any additional setup after loading the view.
}}
End of the Code
I think I am clear, if you have any quires just comment below.
Hi try the following set of code, I have added few additional changes in your code which is necessary for passing the details, I hope it will solve your issue.
I have added only the extra codes which you needed
class ListServicesTVC: UITableViewController {
// Add this variable in this class and use it whereever you needed it in this class
var selectedItem: cellData?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Now maintain the selected data in the local variable we declared
selectedItem = arrayOfCellData[indexPath.row]
// Now perform the segue operation
performSegue(withIdentifier: "VIEW_CONTROLLER_IDENTIFIER_OF_MAP_CLASS", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "VIEW_CONTROLLER_IDENTIFIER_OF_MAP_CLASS" {
let destinationVC = segue.destination as? VIEW_CONTROLLER_IDENTIFIER_OF_MAP_CLASS
destinationVC?.selectedItem = self.selectedItem // Pass the selected item here which we have saved on didSelectRotAt indexPath delegate
}
}
In Second class:
class VIEW_CONTROLLER_IDENTIFIER_OF_MAP_CLASS: UIViewController {
// Add this variable in this class and use it whereever you needed it in this class
var selectedItem: cellData?

how implement a button in customized UITableViewCell in Swift 3?

I tried to use this solution but is not working.
what I want i s a button in my customized cell that knows data from the array that is used from the tableview (later I'll apply it to CoreData), for example, print the value of the array that generated the tableview.
but I cannot understand how to do it
I have a customized cell class, where I tried to use both a button action or a outlet button (with tags):
import UIKit
class MyTableViewCell: UITableViewCell {
#IBOutlet weak var myCellImage: UIImageView!
#IBOutlet weak var myCellLabel: UILabel!
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
}
}
and a ViewController where is my tableview with an extension
extension ViewController {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comicsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = myTableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
cell.myCellLabel.text = String(indexPath.row)
cell.myCellButton.tag = indexPath.row
cell.myCellButton.addTarget(self, action: Selector("logAction:"), for: .touchUpInside)
return cell
}
func logAction(sender: UIButton) {
let titleString = self.comicsArray[sender.tag]
let firstActivityItem = "\(titleString)"
let activityVC = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)
self.present(activityVC, animated: true, completion: nil)
}
}
EDIT:
solved with help of abdullahselek by adding in subclasses cell:
public var dataFromTableView : String!
and implementing:
#IBAction func myCellButtonTapped(_ sender: UIButton) {
guard dataFromTableView != nil else {return}
print("pushed \(dataFromTableView!)")
}
and in cellForRowAt :
cell.data = comicsArray[indexPath.row]
Add a dictionary or object model property to your custom tableviewcell like
public var data: Dictionary!
And set this data property
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.data = comicsArray[indexPath.row]
}
Then you button can reach data with in your tableviewcell

iOS swift UIButton in TableView Cell

I have a tableView with custom cell. in my custom cell I have a like button. for like Button I wrote a function to change state from .normal to .selected like this:
FeedViewCell
class FeedViewCell: UITableViewCell {
#IBOutlet weak var likeButton: UIButton!
var likes : Bool {
get {
return UserDefaults.standard.bool(forKey: "likes")
}
set {
UserDefaults.standard.set(newValue, forKey: "likes")
}
}
override func awakeFromNib() {
super.awakeFromNib()
self.likeButton.setImage(UIImage(named: "like-btn-active"), for: .selected)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func likeBtnTouch(_ sender: AnyObject) {
print("press")
// toggle the likes state
self.likes = !self.likeButton.isSelected
// set the likes button accordingly
self.likeButton.isSelected = self.likes
}
}
FeedViewController :
class FeedViewController: UIViewController {
#IBOutlet var feedTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Register Cell Identifier
let feedNib = UINib(nibName: "FeedViewCell", bundle: nil)
self.feedTableView.register(feedNib, forCellReuseIdentifier: "FeedCell")
}
func numberOfSectionsInTableView(_ tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.feeds.count
}
func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FeedCell", for: indexPath) as! FeedViewCell
return cell
}
}
But my problem is when I tap like button in cell with indexPath.row 0 the state of button in cell with indexPath.row 3 change state too.
where is my mistake?
thanks
You didn't post all your code, but I can tell you that for this to work the #IBAction func likeBtnTouch(_ sender: AnyObject) { } definition must be inside the FeedViewCell class definition to make it unique to a particular instance of the cell.
As a rule of thumb, I normally ensure that all the UI elements inside my cell are populated in cellForRowAtIndexPath when using dequeued cells. Also it should be set from an external source. I.o.w not from a property inside the cell. Dequeuing cells reuse them, and if not setup properly, it might have some leftovers from another cell.
For example, inside cellForRowAtIndexPath:
self.likeButton.isSelected = likeData[indexPath.row]

Resources