Go to the ViewController by clicking on the TableView cell in swift - ios

there is a class MenuViewController in which the array recorded in the table is recorded:
import Foundation
import UIKit
class MenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var menuTableView: UITableView!
let myTitle = ["Помощь", "Информация", "Поддержка"]
override func viewDidLoad() {
super.viewDidLoad()
menuTableView.delegate = self
menuTableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myTitle.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = menuTableView.dequeueReusableCell(withIdentifier: "MenuCell") as! MenuTableViewCell
cell.labelText.text = myTitle[indexPath.row]
return cell
}
}
What do you need to write in it to go to Info Controller?

I assume you are using storyboards. First you need to set up some sort of connection between your two viewcontrollers. You can do this by setting up a "segue". You hold ctrl+click drag from the first viewcontroller to the 2nd viewcontroller. It looks like this:
When you let go of the mouse, you will see this:
Click on Show. Now you will have created a segue.
Now you need to name it. So click on the segue that shows up in the storyboard. It is basically just an arrow from the Menu View Controller to the Info View Controller. On the right hand side you will see a place where you can name it (give it an identifier):
Now ... you have done all you needed to do in the storyboard. Now in your actual code in the MenuViewController, you need to associate clicking the tableviewcell. You do this by using the delegate method didSelectRowAt.
I see you have already set up the delegate with this line:
menuTableView.delegate = self
That ensures that didSelectRowAt will be called whenever the user taps on a row.
So now what you want to do is write the code to perform the segue:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "YourSegueName", sender: self)
}
Note:
Here is more information on interacting with a tableview row:
How to detect tableView cell touched or clicked in swift
Here is more information about segues:
IOS - How to segue programmatically using swift
There are many more customizations you can do ... such as passing data through to the next viewcontroller via the segues. Here's how:
Pass data through segue

implement UITableViewDelegate method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//To InfoViewController I am considering you have created InfoViewController with XIB
let controller = InfoViewController(nibName: "YourNibName", bundle: nil)
//Present Your Controller
self.present(controller, animated: true) {
}
//Push Your controller if your view is already part of NavigationController stack
self.navigationController?.pushViewController(controller, animated: true)
}

Just add a
nextViewController.didMove(toParentViewController: self) inside a
"didSelectRowAt". Example given below.
This is for List a tableview cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "cell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! PatientFamilyDetailTableViewCell
cell.txt_patientName.text = patientname[indexPath.row]
// Configure the cell...
return cell
}
Now it's for Select a Table view cell:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
if cell.isSelected {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "medtestmain") as! medTest
patient_id = relation[indexPath.row]
patient_name = patientname[indexPath.row]
UserDefaults.standard.set(patient_id, forKey: "patient_id")
UserDefaults.standard.set(patient_name, forKey: "patient_name")
self.addChildViewController(nextViewController)
nextViewController.view.frame = self.view.frame
self.view.addSubview(nextViewController.view)
nextViewController.didMove(toParentViewController: self)
}
}
}

Related

Why presentViewController calls viewDidLoad again?

I have ViewController, built using storybard and it contains a UITableView. When UITableViewCell is pressed, I just want to present SomeViewController. In didSelectRowAt method I do following:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
present(SomeViewController(), animated: true)
}
But I checked using debugger, this calls viewDidLoad in ViewController. Here is my viewDidLoad in ViewController:
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
myTableView.dataSource = self
myTableView.delegate = self
}
And Xcode throws an error that myTableView is nil here. What can be reason for this? Seems like I don't know some well known concept in iOS development. It seems like it is not possible to present a SomeViewController from ViewController without segues, because ViewController was completely built using Storyboard, when SomeViewController does not have any storyboard. So, seems like, it is not possible to combine. Another idea is that it is just a Xcode bug. So, what is the reason for the error?
UPDATE:
Here is my ViewController:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
myTableView.dataSource = self
myTableView.delegate = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Hello"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
present(SomeViewController(), animated: true)
}
}
The tableView outlet is nil because your're creating instance of SomeViewController using init like SomeViewController(). This won't connect the outlets in your controller.
Instead create the instance of SomeViewController using the storyboard, i.e.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let controller = self.storyboard?.instantiateViewController(withIdentifier: "SomeVC") as? SomeViewController {
self.present(controller, animated: true, completion: nil)
}
}
Don't forget to set the StoryboardID of SomeViewController as SomeVC in storyboard.
The reason was that I accidentally inherited SomeViewController from ViewController. Not UIViewController. Just ViewController. That of course forced to call viewDidLoad. Will never use default ViewController name.

Each cell open view

How to make Each Cell open the specific view for its indexpath.
In tableview didselect what should i do to make each cell open as its own indexpath so each cell have a different data in the next view
i am tryin when click in a cell in the tableview the next view present it self with it's own data as the next view contain a uitextview like in note app
what should i apply at row didselect
// MARK: -TableFunctions
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return SavingTasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let newtask = self.SavingTasks[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.TheLabel?.text = newtask
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if(editingStyle == .delete)
{
self.SavingTasks.remove(at: indexPath.row)
self.TasksTable.deleteRows(at: [indexPath], with: .fade)
GetData()
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let NewIndex = self.SavingTasks[indexPath.row]
let view = self.storyboard?.instantiateViewController(withIdentifier: "TaskDetail") as! TaskDetail
view.SavingDetails = [NewIndex]
view.Index = indexPath.row
self.navigationController?.pushViewController(view, animated: true)
}
next class should be appeared
class TaskDetail: UIViewController {
var Delegate: NoteDetailDelegate!
var SavingDetails = [String]()
var Index: Int?
#IBOutlet weak var TaskDetailsFiled: UITextView!
#IBAction func SaveTDF(_ sender: UIButton) {
UserDefaults.standard.set(TaskDetailsFiled.text, forKey: "Saved")
self.navigationController?.popViewController(animated: true)
}
You can use a segue and prepare(for:sender:) to get the next view controller ready more easily than instantiating the view controller and popping it via code. Official documentation here and a sample app from Apple here
An implementation example:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mysegue"{
if let nextViewController = segue.destination as? NextViewController{
nextViewController.Index = 2
}
}
}
A highlight from the official doc:
For example, if the segue originated from a table view, the sender parameter would identify the table view cell that the user tapped
If you want to stick with the code implementation, you can call view.myvariable = myvalue in your didSelect

How to go to another View Controller when an element inside table view is being tapped?

I have a UIView inside a TableViewCell,when users tapped in this UIView,it go to another view controller and pass a data along.
So inside TableViewCellClass I done the following :
I set up a protocol and detected the "Tap" gesture,when user table the UIView.This part is working fine:
protocol MyDelegate : class {
func runThisFunction(myString : String)
}
class MyTableViewCell: UITableViewCell {
weak var delegate : MyDelegate?
...other code here
//here detect the tap
let tap = UITapGestureRecognizer(target: self, action: #selector(hereTapped))
self.myUIElementInThisCell.addGestureRecognizer(tap)
}
#objc func hereTapped(sender: UITapGestureRecognizer? = nil){
self.delegate?.runThisFunction(myString: "I am a man")
}
So in my view controller which contain this TableView,I done the following :
I extend out the MyDelegate as subclass,and then attach the protocol function inside it as below
class MyViewController: UIViewController,MyDelagate{
func runThisFunction(myString : String) {
print("Tapped in view controller")
self.performSegue(withIdentifier: "MySegue",sender : self)
}
override func viewDidLoad() {
super.viewDidLoad()
let tableViewCell = MyTableViewCell()
tableViewCell.delegate = self
}
}
Result:
After done all the stuff above,when I tapped the UIView,it didnt perform the segue as stated in MyViewControllerClass,even the print() command also didnt execute.
So what I missing out? Please give me a solution.Thanks
The problem is that the delegate for MyTableViewCell instances is not defined.
When you do:
override func viewDidLoad() {
super.viewDidLoad()
let tableViewCell = MyTableViewCell()
tableViewCell.delegate = self
}
You are setting a delegate for an object that will be destroyed just when the method viewDidLoad() finishes.
Solution 1
In order to avoid this, you have to set the delegate inside the cellForRow method.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! MyTableViewCell
cell.delegate = self
// Other configurations
return cell
}
Solution 2
You can also use the UITableViewDelegate methods in order to capture the user interaction with the cells.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
self.performSegue(withIdentifier: "MySegue",sender : self)
}
This way you avoid all the MyDelegate protocol thing. This would be my preferred option.
The problem is that these 2 lines:
let tableViewCell = MyTableViewCell()
tableViewCell.delegate = self
are not related to the shown cells in the table , it's a cell created on the fly so
set delegate in cellForRow for the cell that you will actually waiting a delegate trigger from them
cell.delegate = self
like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = areaSettTable.dequeueReusableCell(withIdentifier:cellID) as! MyTableViewCell
cell.delegate = self
}
Problem is in your viewDidLoad:
// you create a new cell
let tableViewCell = MyTableViewCell()
// set its delegate
tableViewCell.delegate = self
// and then the cell is not used for anything else
Basically you are not setting the delegate for the cells that are being presented, but for another instance that you create in viewDidLoad.
You have to set a delegate in cellForRowAt to make sure the proper cells get the delegate set:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourIdentifier", for: indexPath) as! MyTableViewCell
cell.delegate = self
return cell
}
This will set the delegate for those cells, that are presented.
Alternatively, I would recommend using didSelectRowAt from UITableViewDelegate (if your MyViewController implements it):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 { // only if it is the first cell (I assume that MyTableViewCell is first)
runThisFunction(myString: "I am a man")
}
}
Delegation in not very good here, indeed if you want to change object that didn't passed to the cell explictitly.
My advice is to use closure:
typealias CellTapHandler = (String)->()
class CustomCell: UITableViewCell {
var handler: CellTapHandler?
#objc func hereTapped(sender: UITapGestureRecognizer? = nil) {
handler?("String or whatever you want to get back from cell.")
}
//...
}
and set up it from view controller
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourIdentifier", for: indexPath)
(cell as? MyTableViewCell).handler = { [weak self] string in }
return cell
}
PS: As I said, usually you want to pass object related to cell further. It's difficult to do with delegation, as you had to pass to cell some additional token, to determine object by the cell, o pass object itself breaking Model-View separation paradigm. But it can be done easily with closures:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let object = self.myData[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "yourIdentifier", for: indexPath)
(cell as? MyTableViewCell).handler = { [weak self] string in
self.performSegue(withIdentifier: "MySegue",sender : object)
}
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (let myObj = sender as? MyObjType, let subsequentVC = segue.destination as? NextViewController) {
subsequentVC.selectedMyObject = myObj
}
}

How to redirect to another view controller when click on TableViewCell?

I can't figure why it can't redirect to another View Controller when I click on a TableViewCell
FirstViewController is the view controller that contains the tableView
SecondViewController is my desination
What I already done:
1) I embedded FirstViewController in a NavigationController in StoryBoard
2) I already set up show segue from FirstViewController to SecondViewController, and already set the segue identifier to "GoToSecondViewController"
Of course in order for me to show the data in the tableViewCell I have this 2 function:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.myCell.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCellClass
cell.myCellList = self.myCell[indexPath.row]
return cell
}
3) In FirstViewController, I already try for below 2 solution:
First solution:
I call performSegue in didSelectRowAt function
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "GoToSecondViewController", sender: self)
}
Second solution:
I use navigationController?.pushViewController in didSelectRowAt function
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
self.navigationController?.pushViewController(controller, animated: true)
}
Above 2 solution I tried, but didn't produce any result. After looking at this question, I even disable my gestureRecognizer in FirstViewController, still cant redirect to SecondViewController
So I finally just wanna to print out the value by this,but also no value printing out in console:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("section: \(indexPath.section)")
print("row: \(indexPath.row)")
}
That all I tried in order to redirect to another view controller when click on tableViewCell, but still not working.What I missing here?
add this line in viewDidLoad
tableview.delegate = self
I finally figure it out,I need to check the Single Selection option for TableView in StoryBoard.
Like this:
Previously I set the Selection to No Selection.Therefore causing the problem above.

Xcode 8 Table View Cells to Different Views

I am having trouble with linking view controllers to table view cells. Basically I have 4 cells in my Table View, and I want each cell to present a separate view controller when clicked.
Here are my swift codes:
import UIKit
var orchard = ["Dog", "Cat", "Bird", "Fish"]
var myIndex = 0
class AnimalList: UITableViewController {
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return orchard.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = orchard[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
myIndex = indexPath.row
performSegue(withIdentifier: "segue", sender: self)
}
}
Here is my storyboard, as you can see I have 4 view controllers that I want to connect to the 4 table view cells.
storyboard
PS: My table view controller has already been embedded inside a navigation controller.
How do I do that? Thank you!
Embedded your TableViewController in Navigation Controller ( Most probably , you already did ) .
Select Yellow marked TableViewController click Right Mouse hold and put it to a view controller , release and select Manual Segue -> Show
Now select segue and rename it with unique Identifier . For every view controller repeat steps 2 & 3 .
Update tableView didSelectRowAt delegate function like below .
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath)
{
performSegue(withIdentifier: orchard[indexPath.row], sender: self)
}
Give a segue for the four ViewControllers and name it as vc1Segue, vc2Segue, vc3Segue, vc3Segue
And change your didSelectRowAt method like below
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
myIndex = indexPath.row
switch myIndex {
case 0:
performSegue(withIdentifier: "vc1Segue", sender: self)
case 1:
performSegue(withIdentifier: "vc2Segue", sender: self)
case 2:
performSegue(withIdentifier: "vc3Segue", sender: self)
case 3:
performSegue(withIdentifier: "vc4Segue", sender: self)
default:
print("Index greater than vcs")
}
}
Just now i done demo project with your requirements its working perfectly.
Step1: Prepare your story board as shown in below image.
step2: Give storyboard identifiers as dogID,catID,birdID,fishID to the respective viewController as shown in below image.
step3: Declare an array with the above identifiers just below your arched array.
var storyboardIds = ["dogID","catID","birdID","fishID"]
step4: Modify your didSelectRowAt method as shown in below code snippet.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
myIndex = indexPath.row
if let vc = storyboard?.instantiateViewController(withIdentifier: storyboardIds[myIndex]) {
self.navigationController?.pushViewController(vc, animated: true)
}
}
Create a segue for each of the 4 view controllers by ctrl+right click and dragging from table view controller to each of the view controllers. Give an identifier to the segues (e.g. "TableToViewController1", "TableToViewController2", etc)
Then in your didSelectRowAt do something like:
var segueIdentifier = ""
switch(indexPath.row) {
case 0: // first cell is selected
segueIdentifier = "TableToViewController1"
case 1:
segueIdentifier = "TableToViewController2"
case 2:
segueIdentifier = "TableToViewController3"
case 3:
segueIdentifier = "TableToViewController4"
}
performSegue(withIdentifier: segueIdentifier, sender: self)
What you can do is inside your
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
myIndex = indexPath.row
performSegue(withIdentifier: "segue", sender: self)
}
func you can check which index it is and based on that present the viewController you want.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
myIndex = indexPath.row
if let animal = orchard[indexPath.row] as String? {
if animal == "dog" {
performSegue(withIdentifier: "dogViewController", sender: self)
}
}
}
In your storyboard you would create a segue from the viewController to all ur viewControllers and name the segues with something like dogViewController etc.

Resources