UIViewController when UITableViewCell is Pressed - swift

I am trying to make an app so that when the cell in the UITableView is pressed, it opens a new ViewController with the same image and label used before in the initial cell that was clicked.
Here are my cells:
Here is the array I am using:
let alligator:NSArray = ["American Alligator","American Crocodile","Mugger Crocodile","Saltwater Crocodile"]
Here is the numberOfRowsInSection and cellForRowAt:
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return(alligator.count)
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell4 = tableView.dequeueReusableCell(withIdentifier: "cell4", for: indexPath) as! AlligatorViewCell
cell4.alligatorImage.image = UIImage.init(named: alligator[indexPath.row] as! String + ".jpg")
cell4.alligatorLabel.text = alligator[indexPath.row] as? String
return(cell4)
}
Here are my outlets for the detail View Controller:
#IBOutlet var detailImage: UIImageView!
#IBOutlet var detailLabel: UILabel!
And here is the didSelectRowAt
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
detailImage.image = UIImage.init(named: alligator[indexPath.row] as! String + ".jpg")!
detailLabel.text = alligator[indexPath.row] as? String
The problem is that when I press the cell, I get the error, fatal error: Unexpectedly found nil while Unwrapping Optional

I believe your error is occurring because detailImage does not exist when you call it.
From what I understand, these outlets are in your base view controller, but they are referring to objects in your detail view controller, which isn't allowed. Thus when you try and call them, they won't pass anything giving a value of nil.
Thus it is not about the UIImage being initialized.
You need to make the IBOutlets within the detailViewController class, and then pass the values from your cell to these outlets through your prepareForSegue func.
Let me know if you have questions about this.
Edit
Let's say your detail view controller has the detailImage property, then you could do something like this (in your base ViewController)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let controller = segue.destination as! UIViewController //your detail controller
controller.detailImage = myImage //where myImage is a property of your base view controller
}
Edit
You could set the myImage property in the view controller when your cell is tapped just as your tried before. Then myImage could be set to the detailImage in the prepareForSegue func. So something like this:
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myImage = UIImage.init(named: alligator[indexPath.row] as! String + ".jpg")!
...
Edit
You should be using myImage like this:
var myImage: UIImage?
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myImage = UIImage.init(named: alligator[indexPath.row] as! String + ".jpg")!
...

Related

My function tableView.didSelectRow is not working

I have a tableView with custom cells and my goal is to be able to click on one and print the number of the current row.
Right now when I click on a cell not happens and I can't understand why.
This is the code:
//MARK: - UITableDataSource
extension ChannelViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return groupResult.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let group = groupResult[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: K.cellContactNibName, for: indexPath) as! ContactCell
cell.usernameLabel.text = group.name
cell.messageLabel.text = group.recentMessage
let storage = Storage.storage()
let storageRef = storage.reference()
// Reference to an image file in Firebase Storage
let reference = storageRef.child("imagesFolder/\(group.image)")
// UIImageView in your ViewController
let imageView: UIImageView = cell.uImage
// Placeholder image
let placeholderImage = UIImage(named: "MeAvatar")
// Load the image using SDWebImage
imageView.sd_setImage(with: reference, placeholderImage: placeholderImage)
cell.uImage.image = imageView.image
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.row)!")
}
}
and as you can see in the screen the single selection is enabled:
change to
extension ChannelViewController: UITableViewDataSource, UITableViewDelegate
or move the method into another extension conformed to UITableViewDelegate
also, remember to set the tableView's delegate to the ChannelViewController

UITableViewCell not updating UISwitch when selecting row

I have a UITableViewCell that contains a bunch of UISwitches. I want the switch to toggle on/off based on what row the user selects, the data is passing to my cell but the switch state is not updating.
I am using a basic MVC, the Storyboard has a TableView > TableViewCell > Label | UISwitch
Controller:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var testList = [1,2,3,4,5]
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table.tableFooterView = UIView()
table.delegate = self
table.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
//Turn them all on at start
cell.setSwitch(rowSelected: true)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
cell.setSwitch(rowSelected: false)
}
}
SwitchCell
class SwitchCell: UITableViewCell {
#IBOutlet weak var uiswitch: UISwitch!
#IBOutlet weak var label: UILabel!
func setCell(number: Int){
label.text = String(number)
}
func setSwitch(rowSelected:Bool) {
uiswitch.setOn(rowSelected, animated: true)
}
}
I know I can just make the UISwitch intractable, but I am looking at changing it's state when the user selects the row.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
cell.setSwitch(rowSelected: false)
}
is incorrect. Using this code you don't pick the cell you want.
You should do simething like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.cellForRow(at: indexPath) as! SwitchCell
cell.setSwitch(rowSelected: false)
}
First of all, we don't dequeue the cell in didSelectRowAt method. Never do that. This will dequeue a brand new cell and won't reflect any changes that you do in it.
So remove the code for tableView(_: didSelectRowAt:) method.
Secondly, you can simply handle the UISwitch state based on cell selection in SwitchCell's definition using setSelected(_:animate:) method like so,
class SwitchCell: UITableViewCell {
#IBOutlet weak var uiswitch: UISwitch!
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
uiswitch.setOn(selected, animated: true)
}
//rest of the code...
}
To avoid bulky code, do all the cell layout in custom cell itself instead of doing it in the delegate or dataSource methods. That will only make you ViewController heavier and non-modular.

How can I call the action of a checkbox button when the cell is touched?

I'm using a custom cell which contains a label and a button. In my button, i've an action associated with it i.e when the button is touched the button changes its image and changed to checked image. But i want that action to be call when entire cell is touched.
extension HostOptionViewController: UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Question.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let question = Question[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionOptionCell") as! ViewCellTableViewCell
cell.setText(option: question)
arrayOfCells.append(cell)
return cell
}
In my Custom cell
import UIKit
class ViewCellTableViewCell: UITableViewCell {
#IBOutlet weak var DataCell: UILabel!
#IBOutlet weak var checkBox: UIButton!
#IBAction func CheckBoxFunc(_ sender: Any) {
if checkBox.isSelected{
checkBox.isSelected = false
}
else{
checkBox.isSelected = true
}
}
func setText(option: Data){
DataCell.text = option.data
}
}
I want to call "CheckBoxFunc" when the cell is touched.
If you want the CheckBoxFunc to be called when selecting cell, then you should call it in didSelectRowAtIndexPath like:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Get your Custom cell here using dequeueReusableCell
// Call the CheckBoxFunc
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell: ViewCellTableViewCell = tableView.cellForRow(at: indexPath)! as! ViewCellTableViewCell
selectedCell.checkBox.addTarget(self, action: #selector(TableViewController.checkBoxTapped(_:)), for: .touchUpInside)
}
func checkBoxTapped(sender:UIButton) {
let question = Question[sender.tag]
print(question)
}
Don’t forget to add tag for checkbox button in cellForRowIndex method .

Connect Prototype Cells to View Controller [Swift 4]

I am new to programming and currently working on a newsfeed like app. I had a normal Table view up and running fine, but want to try it now with a costume cell type. So I created one and thought connecting the labels the usual way would be perfectly fine, but I was wrong. So I am wondering how I can let my Text label connect to my view controller, so I can use my custom cell.
class ViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var newsfeedTableView: UITableView!
var ref: DatabaseReference!
var posts = [String]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (posts.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "cell")
//here is where I need the custom label to get the posts
cell.textLabel?.text = posts[indexPath.row]
cell.textLabel?.font = UIFont.boldSystemFont(ofSize: 18.0)
return cell
}
}
Create subclass of UITableViewCell and connect IBOutlets to this class
class YourCell: UITableViewCell {
#IBOutlet weak var customLabel: UILabel!
...
}
don't forget to set class of your prototype cell in storyboard:
then in cellForRowAt data source method downcast your dequeued cell as YourCell
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! YourCell
then you have access to YourCell outlets
cell.customLabel.text = "SomeText"
...
I'm assuming that you are using Storyboard.
First of all, you should understand that there is little difference when you use own custom table cell. In that case, in the method "cellForRowAtIndexPath", after dequeue your cell, you just have to typecast table cell like 'as! YourCustomTableCellClass'. After this line, you can access each property of this class.
First, design your table cell on Storyboard whatever you want.
Now, make a subclass of UITableViewCell and assign this class to your prototype custom cell which you have designed on Storyboard. Also, don't forget to set "reuse identifier in Storyboard table cell.
Then connect your outlets with custom cell class from Storyboard.
Now you can use code like this:
class YourTableCellClass: UITableViewCell {
// I'm using these outlets as a sample, but you have to connect them from Storyboard.
var leftTextLabel: UILabel!
var rightTextLabel: UILabel!
}
class YourTableController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - TableView Delegate & DataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1 // return your number of rows here...
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100 // return the height for the row here.....or you can set height from Storyboard if you have fix height of all rows.
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath) as! YourTableCellClass
cell.leftTextLabel.text = "Text" // Set text here....
cell.rightTextLabel.text = "Text" // Set text here....
return cell
}
}

click on TableViewCell to open profile view of current user

I am using firebase database and have a table view showing name, age, location, and image of user at index path.row. How would I go about showing another view when the cell is clicked to the users profile with more information and photos of the user. here is the code for my tableView.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "prefCell", for: indexPath) as! SFPTableViewCell
let user = userList[indexPath.row]
cell.name.text = userList[indexPath.row].name
cell.location.text = userList[indexPath.row].location
if let imageURL = user.image {
cell.imageOne.loadImageUsingCacheWithUrlString(urlString: imageURL)
}
return cell
}
}
Using Code use didSelectRowAtIndexPath.
or
Using Storyboard:
step 1: select tableview cell.
step 2: Control + click mouse and drag in destination view.
step 3: Give Any of segue.
step 4: Done.
Add this below code
override func tableView(_ tableView: UITableView, didSelectRowAtindexPath: IndexPath){
let detailVC = storyboard.instantiateViewControllerWithIdentifier("MyDetailVC") as MyDetailVC // get the obect of other VC
detailVC.myImage = <your_image> // assign your image which you wanted to send to other VC.
self.navigationController?.pushViewController(detailVC,animated: true // Push to other VC
}
in MyDetailVC.swift
var myImage : UIImage? // Create variable of image type in required VC where you wanted to send data
Now use your Image in MyDetailVC.swift.
Any further query you can ask.
Just used UITableViewDelegate method "didSelectRowAtIndexPath" and send image to other view controller with its indexPath
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sb = UIStoryboard(name: "Main", bundle: nil)
let detailVC = sb.instantiateViewController(withIdentifier: "ImageVC") as! DetailViewController
detailVC.strImage = self.arrImage[indexPath.row]
self.navigationController?.pushViewController(detailVC, animated: true)
}
In DetailViewController take one variable
var strImage : String = ""
and assgin this variable to UIImagView
imgView.image = UIImage(named: strImage)