I am working on a iOS app that has two ViewControllers. The first is a TableView which creates a row for each index in a array. Each cell of this TableView shows the content in the array corresponding to the index and has a switch. The second ViewController has an image and a label and they are supposed to change depending the switch state. So, how can I get the switch state from a specific cell?
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var state_label: UILabel!
#IBOutlet weak var descr_label: UILabel!
#IBOutlet weak var myimg: UIImageView!
let arr: [String] = ["Text1","Text2", "Text3", "Text4"]
var switch_isOn = false
override func viewDidLoad() {
super.viewDidLoad()
if(switch_isOn == false){
myimg?.image = UIImage(named: "img1")
}else{
myimg?.image = UIImage(named: "img2")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = arr[indexPath.row]
let mySwitch = UISwitch()
cell.accessoryView = mySwitch
mySwitch.tag = 1001
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let segueIdentifier: String
segueIdentifier = "segue"
// Get the selected Cell and Iterate through it's subviews to find the switch using the tag
let cell = tableView.cellForRow(at: indexPath)
//Get the Cell Text
print("\(cell?.textLabel?.text ?? "")")
// Iterate through subviews of Cell
for v in cell?.subviews ?? [] {
// If a view found with tag == 1001 then it's the switch view because we had assigned 1001 to the switch view
if v.tag == 1001 {
// One last check we cast the view to UISwitch if it succeed then it's the switch view
if let mySwitch = v as? UISwitch {
if(mySwitch.isOn == true){
descr_label?.text = "\(cell?.textLabel?.text ?? "")"
print("The cell has the Switch On")
switch_isOn = true
}else{
descr_label?.text = "\(cell?.textLabel?.text ?? "")"
switch_isOn = false
print("The cell has the Switch Off")
}
}
}
}
self.performSegue(withIdentifier: segueIdentifier, sender: self)
}
}
Using the accessory view for the switch seems to be an easy solution but it's very cumbersome to access the view. Something like for v in cell?.subviews ?? [] and dealing with tags is horrible.
A better more efficient solution is a custom cell class.
In Interface Builder set the style of the cell to custom and drag an UILabel and an UISwitch into the canvas. Set the class of the cell to TableViewCell.
Add a new CocoaTouch class TableViewCell as subclass of UITableViewCell. You need two IBOutlets, one IBAction and a callback variable. The callback is important to keep the state of the switch in the model. Connect the outlets and the action in IB.
class TableViewCell: UITableViewCell {
#IBOutlet weak var switcher : UISwitch!
#IBOutlet weak var label : UILabel!
var callback : ((Bool)->())?
#IBAction func switchChanged(_ sender : UISwitch) {
callback?(sender.isOn)
}
}
Create a data source model containing the text and the state of the switch
struct Item {
var text : String
var isSelected : Bool
init(text : String, isSelected : Bool = false {
self.text = text
self.isSelected = isSelected
}
}
Declare the data source array
var arr : [Item] = [Item(text: "Text1"), Item(text: "Text2"), Item(text: "Text3"), Item(text: "Text4")]
Replace cellForRow with
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
let item = arr[indexPath.row]
cell.label.text = item.text
cell.switcher.isOn = item.isSelected
// the callback updates the model and is called when the value of the switch changes
cell.callback = { newValue in
item.isSelected = newValue
}
return cell
}
Replace didSelectRow with (yes, it's only one line, it passes the index path as sender parameter)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "segue", sender: indexPath)
}
Finally Implement prepare(for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue" {
let viewController = segue.destination as! ViewController // the class of the second view controller
// get the current index path
let indexPath = sender as! IndexPath
let item = arr[indexPath.row]
// get the state of the switch from the model, not from the view
let isSelected = item.isSelected
// do something with `isSelected`
}
}
}
In order to achieve what you want properly, you're going to want to set up a custom cell.
An example of this is below, and assumes a Storyboard/XIB UI:
import UIKit
class SwitchTableViewCell: UITableViewCell {
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var contentSwitch: UISwitch!
// So that we can identify the cell in our table view controller.
static let identifier: String {
return String(describing: type(of: self))
}
}
in order to use this with your table view. you will have to register the cell for use in SwitchTableViewController.viewDidLoad():
tableView.register(SwitchTableViewCell.self, forCellReuseIdentifier: SwitchTableViewCell.identifier)
Next, you're going to want to modify cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(SwitchTableViewCell.identifier, forIndexPath: indexPath) as! SwitchTableViewCell
cell.textLabel?.text = arr[indexPath.row]
// cell.contentSwitch will be setup as an outlet via Storyboard / XIB.
return cell
}
after that's done, go ahead and add a variable to SwitchTableViewController:
fileprivate var selectedState: UIControl.State?
And update didSelectRowAt to store the state from the cell:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! SwitchTableViewCell
selectedState = cell.contentSwitch.state
segueIdentifier = "segue" // probably want a more meaningful segue name here.
self.performSegue(withIdentifier: segueIdentifier, sender: self)
}
finally, override prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue" {
if let vc = segue.destination as? ContentViewController { // cast accordingly, 'ContentViewController' is placeholder
// pass the state to the destination view controller
vc.state = selectedState
selectedState = nil
}
}
}
and that's you done!
There are many ways to read the state of a switch from a cell. You can create a custom cell class and access that using IBOutlets and even you can use delegates to get back from the Custom Cell class back to your View Controller. If you're using this code for learning purposes it's Ok to use and add any types of Controls to the cell like this but in real project you might try Custom cells.
See the commented areas in the code
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var myimg: UIImageView!
var arr: [String] = ["bla", "blablabla", "blabla"]
override func viewDidLoad() {
super.viewDidLoad()
myimg?.image = UIImage(named: "Image1")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default,
reuseIdentifier: "cell")
cell.textLabel?.text = arr[indexPath.row]
let mySwitch = UISwitch()
// Add a tag to your switch so later on you can access the switch using this tag
mySwitch.tag = 1001
cell.accessoryView = mySwitch
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
segueIdentifier = "segue"
// Get the selected Cell and Iterate through it's subviews to find the switch using the tag
let cell = tableView.cellForRow(at: indexPath)
// Iterate through subviews of Cell
for v in cell?.subviews ?? [] {
// If a view found with tag == 1001 then it's the switch view because we had assigned 1001 to the switch view
if v.tag == 1001 {
// One last check we cast the view to UISwitch if it succeed then it's the switch view
if let mySwitch = v as? UISwitch {
// Here you can get the state of the switch
let switchState = mySwitch.state
}
}
}
self.performSegue(withIdentifier: segueIdentifier, sender: self)
}
}
As I said this is not the best way to add and read views using tags but still good to know that you can
Edit:
Here is the complete solution for your project to work. You already have a ViewController but you don't have a DetailViewController to which you want to segue
View Controller Code
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let rooms: [String] = ["Kitchen","Living Room", "Master's Bedroom", "Guest's Bedroom"]
let segueIdentifier = "segueIdentifier"
var switch_isOn = false
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rooms.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = rooms[indexPath.row]
let mySwitch = UISwitch()
cell.accessoryView = mySwitch
mySwitch.tag = 1001
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Get the selected Cell and Iterate through it's subviews to find the switch using the tag
let cell = tableView.cellForRow(at: indexPath)
// Iterate through subviews of Cell
for v in cell?.subviews ?? [] {
// If a view found with tag == 1001 then it's the switch view because we had assigned 1001 to the switch view
if v.tag == 1001 {
// One last check we cast the view to UISwitch if it succeed then it's the switch view
if let mySwitch = v as? UISwitch {
// Assign the current state of the switch to switch_isOn variable
self.switch_isOn = mySwitch.isOn
}
}
}
self.performSegue(withIdentifier: segueIdentifier, sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueIdentifier {
if let detailViewController = segue.destination as? DetailViewController {
// used guard let to be on safe side
guard let indexPath = sender as? IndexPath else { return }
// pass in the data needs to the detail view controller
detailViewController.descr = rooms[indexPath.row]
detailViewController.isOn = switch_isOn
}
}
}
}
Detail View Controller Code
import UIKit
class DetailViewController: UIViewController {
#IBOutlet weak var descr_label: UILabel!
#IBOutlet weak var state_label: UILabel!
#IBOutlet weak var myImageView: UIImageView!
var descr = ""
var isOn = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
descr_label.text = descr
// for UIImage you can use UIImage(named: "on_image") but i have used the imageLiteral which is pics directly the image from xcassets
myImageView.image = isOn ? #imageLiteral(resourceName: "on_image") : #imageLiteral(resourceName: "off_image")
state_label.text = isOn ? "Switch is ON" : "Switch is Off"
}
}
Example Project download here
Related
If I click button I want to pass label in button to the next view. Next view need navigationItem title with label from buttons in previous view.
I've tried segues, but it didn't work. I am trying with instantiate view now, but it also doesn't work (next View appear but without title and needed data)
**HomeTableViewController**
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "workoutFirstCell", for: indexPath) as! WorkoutTableCell
cell.workoutField?.setTitle(workouts[indexPath.row].workoutTitle, for: .normal)
cell.index = indexPath
cell.cellDelegate = self
return cell
} func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "workoutFirstCell", for: indexPath) as! WorkoutTableCell
cell.workoutField?.setTitle(workouts[indexPath.row].workoutTitle, for: .normal)
cell.index = indexPath
cell.cellDelegate = self
return cell
}
extension HomeTableViewController:WorkoutTableCellDelegate {
func onClickCell(index: Int) {
if let vc = storyboard?.instantiateViewController(withIdentifier: "allWorkouts") as? AllWorkoutsViewController {
vc.titleValue = workouts[index].workoutTitle
vc.tableView.reloadData()
}
}
}
Here is my cell controller
protocol WorkoutTableCellDelegate {
func onClickCell(index: Int)
}
class WorkoutTableCell: UITableViewCell {
var cellDelegate:WorkoutTableCellDelegate?
var index: IndexPath?
var item = AllWorkoutsViewController().navigationItem.title
#IBOutlet weak var workoutField: UIButton!
let user = Auth.auth().currentUser
let db = Firestore.firestore()
var models: [DataCell] = []
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func buttonTapped(_ sender: Any) {
cellDelegate?.onClickCell(index: (index?.row)!)
}
And View Controller where I want to display navigationItemTitle with label from button in previous view
class AllWorkoutsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var titleValue: String = ""
let user = Auth.auth().currentUser
var db = Firestore.firestore()
var models: [DataCell] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
navigationItem.title = titleValue
You should try this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: nil)
if segue.identifier == "youSegue" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let controller = segue.destination as! YourViewController
controller.title = workouts[indexPath.row]
}
}
}
Essentially I have a view controller called FirstViewController, this view controller contains a table view within it called listTableView.
I would like to tap on one of the cells in the table view listTableView and present whatever text was in the cell as the navigation controller title.
The navigation controller that appears when the cell is tapped is called showDetailsViewController.
How can this be done?
The following is what I have written in the FirstViewController
import UIKit
import AudioToolbox
class FirstViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, FeedModelProtocol {
var feedItems: NSArray = NSArray()
var selectedStock : StockModel = StockModel()
let tableView = UITableView()
#IBOutlet weak var listTableView: UITableView!
#IBOutlet weak var refreshButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
//set delegates and initialize FeedModel
self.listTableView.delegate = self
self.listTableView.dataSource = self
let feedModel = FeedModel()
feedModel.delegate = self
feedModel.downloadItems()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
#IBAction func reloadData(_ sender: Any) {
print("reload pressed")
listTableView.reloadData()
viewDidLoad()
_ = AudioServicesPlaySystemSound(1519)
}
func itemsDownloaded(items: NSArray) {
feedItems = items
self.listTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
print("item feed loaded")
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "stockCell"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
myCell.textLabel?.textAlignment = .center
myCell.textLabel?.font = .boldSystemFont(ofSize: 18)
// Get the stock to be shown
let item: StockModel = feedItems[indexPath.row] as! StockModel
// Configure our cell title made up of name and price
let titleStr = [item.customer].compactMap { $0 }.joined(separator: "-")
print(titleStr)
// Get references to labels of cell
myCell.textLabel!.text = titleStr
return myCell
}
}
UPDATE:
What is the issue with this code:
NOTE:
The restoration id of the tableview is scheduleTable
var homeworkIdentifierFromTableViewCell = ""
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
homeworkIdentifierFromTableViewCell = feedItems[indexPath.row].myCell
self.performSegue(withIdentifier: "scheduleTable", sender: self)
listTableView.deselectRow(at: indexPath, animated: true)
}
UPDATE 2
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item: StockModel = feedItems[indexPath.row] as! StockModel
let titleStr = [item.customer].compactMap { $0 }.joined(separator: "-")
print(titleStr)
}
You can use the didSelectRowAt to notice what cell was clicked and store what the text in the cell was (homeworkArray is the list of cells from a struct. Homeworkidentifier is a value in the struct).
var homeworkIdentifierFromTableViewCell = ""
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
homeworkIdentifierFromTableViewCell = homeworkArray[indexPath.row].homeworkIdentifier
self.performSegue(withIdentifier: "homeworktoExpandHomework", sender: self)
homeworkTableView.deselectRow(at: indexPath, animated: true)
}
Then, you could use a prepare for a segue function to pass the text of the table view cell to the next view controller. You do this by creating a variable in the other view controller (the one that you are going to pass data to) and later accessing it from the other view controller and changing its value.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "reportBug" {
let destinationViewController = segue.destination as! WebViewController
destinationViewController.reason = "reportBug"
}
else if segue.identifier == "provideFeedback" {
let destinationViewController = segue.destination as! WebViewController
destinationViewController.reason = "provideFeedback"
}
}
Here is more about passing data between viewcontrollers : Passing data between View Controllers in Swift (From TableView to DetailViewController)
Hope this helps
EDIT:
Here is the struct I am using :
struct homeworkTableViewCellData {
let homeworkName : String!
let className : String!
let dateName : String!
let colorImage : UIImage!
let homeworkIdentifier : String!
}
I have initialized my homeworkArray with this struct. When I am calling a value from the cell, I am picking one from in the struct.
To set the table view with a struct is more organized. This is a good video that teaches you how to set it up (if you are want to do that) : https://www.youtube.com/watch?v=zAWO9rldyUE&list=LL--UalPCi7F16WzDFhMEg7w&index=20&t=921s
I have two custom table views. I need to pass first and second cell datas of DestinationTableView to first cell of MyCartTableView. How can I make transition between this two table view cells with outside of tableView.
I did tableView.indexPathForSelectedRow but this time I need to make with UIButtonoutside of tableView.
Below triggered with tableView cell.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cellForFoodSegue" {
if let destinationViewController = segue.destination as? DetailViewController
{
let indexPath = self.mainTableView.indexPathForSelectedRow!
var foodNameArray: String
var foodPriceArray: Double
foodNameArray = foodNames[indexPath.row]
foodPriceArray = foodPrices[indexPath.row].purchaseAmount
destinationViewController.detailFoodName = foodNameArray
destinationViewController.detailFoodPrice = foodPriceArray
}
}
}
I tried below code but I did not success passing data with button.
#IBAction func addBasket(_ sender: Any) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "addToCartSegue") {
if let addToCartVC = segue.destination as? MyCartViewController {
let selectedCell = sender as! UITableViewCell
let indexPath = self.detailTableView.indexPath(for: selectedCell)
var foodNameArray: String
var foodPriceArray: Double
foodNameArray = foodNames[indexPath.row]
foodPriceArray = prices[indexPath.row].purchaseAmount
addToCartVC.fromDetailFoodName = foodNameArray
addToCartVC.fromDetailFoodPrice = prices[(indexPath?.row)!].purchaseAmount
}
}
}
Belows my MyViewController codes. Which is my added objects when tapped to addBasket button
class MyCartViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var fromDetailFoodName: [String?] = []
var fromDetailFoodPrice = Double()
var nameLabel = MyCartTableViewCell()
#IBOutlet weak var myCartTableView: UITableView!
#IBOutlet weak var totalPriceLabel: UILabel!
let foodNames = [
"Hamburger big mac",
"Cemal",
"Emre",
"Memo"
]
//TODO-: Delete my cart
#IBAction func deleteMyCart(_ sender: Any) {
}
//TODO: - Approve my cart
#IBAction func approveCart(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return section == 0 ? 1 : foodNames.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCartCell", for: indexPath) as! MyCartTableViewCell
cell.myCartFoodNameLabel?.text = fromDetailFoodName.description
cell.myCartFoodPriceLabel?.text = "\(fromDetailFoodPrice)₺"
return cell
}
}
You should get the index path of the data you want to pass in func addBasket(_ sender: Any).
For example, you can save index path as a property that referenced in class.
class StartViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var selectedIndexPath: IndexPath?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndexPath = indexPath
}
#IBAction func addBasket(_ sender: Any) {
if let indexPath = selectedIndexPath {
let destinationVC = MyCartViewController()
destinationVC.detailFoodName = foodNames[indexPath.row]
destinationVC.detailFoodPrice = foodPrices[indexPath.row].purchaseAmount
}
}
}
In MyCartViewController which is destination VC.
class MyCartViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var fromDetailFoodNames: [String?] = []
var fromDetailFoodPrices: [Double?] = []
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 1 && indexPath.last! <= fromDetailFoodPrices.indices.last! {
let cell = myCartTableView.dequeueReusableCell(withIdentifier: "myCartCell", for: indexPath) as! MyCartTableViewCell
let name = fromDetailFoodNames[indexPath.row]?.description ?? ""
let price = fromDetailFoodPrices[indexPath.row]
cell.myCartFoodNameLabel?.text = name
cell.myCartFoodPriceLabel?.text = "\(price)₺"
return cell
}
}
}
BTW, for better coding, you can implement OOP concept in your code. detailFoodName and detailFoodPrice should be in ONE object. Besides, var foodNameArray: String naming could be confusing. Rename it as var foodName: String would be better.
I have a problem and can't seem to fix it after looking at tutorials online and other SO questions with a similar problem, which leaves me to think I've done something wrong/bad practice related in my code.
I have 2 table view controllers.
The first TableViewController is populated from a database, all this works fine. When I click one of the cells it segues to a second TableViewController which also should be populated from a database (depending on what you select in the first VC).
Currently if I click a cell in TVC1 it goes to TVC2 and it's empty, then it I click back within my navigation controller and select something else, it goes back to TVC2 and shows me my first selection. This indicates that TVC2 is being loaded before the network has returned its data from the database.... so, I tried using tableView.reloadData() in various places like viewDidLoad and viewDidAppear, but i just can't seem to get it to work.
Below is both TVC's. I've stuck with MVC design pattern and haven't included the model and severConnection code for each TVC because I don't want to over complicate the post, however if you'd like to see either I will update.
Thanks in advance for any help.
TableViewController1
class MenuTypeTableViewController: UITableViewController, MenuTypeServerProtocol {
//Properties
var cellItems: NSArray = NSArray()
var selectedItem = String()
override func viewDidLoad() {
super.viewDidLoad()
let menuTypeServer = MenuTypeServer()
menuTypeServer.delegate = self
menuTypeServer.downloadItems()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier: String = "cellType"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
let item: MenuTypeModel = cellItems[indexPath.row] as! MenuTypeModel
myCell.textLabel?.text = item.type
return myCell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath)
selectedItem = (selectedCell?.textLabel?.text)!
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "typeItems" {
let destinationVC = segue.destination as? TypeItemsTableViewController
destinationVC?.selectedItem = self.selectedItem
}
}
}
TableViewController2:
class TypeItemsTableViewController: UITableViewController, TypeItemsServerProtocol {
//Properties
var cellItems: NSArray = NSArray()
var selectedItem: String = String()
let typeItemsServer = TypeItemsServer()
override func viewDidLoad() {
super.viewDidLoad()
typeItemsServer.delegate = self
self.typeItemsServer.foodType = self.selectedItem
self.typeItemsServer.downloadItems()
self.tableView.reloadData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier: String = "cellTypeItem"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
let item: TypeItemsModel = cellItems[indexPath.row] as! TypeItemsModel
myCell.textLabel?.text = item.name!
return myCell
}
}
Try adding this to TypeItemsTableViewController
override func viewDidLoad() {
super.viewDidLoad()
cellItems = NSArray()//make sure you have the empty array at the start
typeItemsServer.delegate = self
self.typeItemsServer.foodType = self.selectedItem
self.typeItemsServer.downloadItems()
self.tableView.reloadData()
}
and
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
typeItemsServer.delegate = nil
}
Add this at the top
var cellItems: NSArray = NSArray() {
didSet {
tableview.reloadData()
}
}
Now you can remove other tableview.reloadData() calls since it will automatically be called once cellItems are set...
I think you have a timing problem. You're reloading right after your async data call. You reload but your data isn't in place at that time. Try using functions with escaping or use "didSet" on your data like:
var dataArray: [type] {
didSet {
tableview.reloadData()
}
}
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?