storyboard elements not visible in view controller after segue - swift

I added a view controller (vc2) with some simple form elements to it to my storyboard and made a segue ctrl-dragging from an existing tableviewcontroller (vc1) which is triggered by a trailing swipe button
I can see output from the print function (in the vc2 code below )in the debugger but my form elements aren't visible. And for some reason I had to manually set the background color, which had defaulted to black, not what was set on storyboard. I think this is related to the way I am loading vc2, but my attempts to change the code to a normal performSegueWithIdentifier caused a
'NSInvalidArgumentException', reason: 'Receiver () has no segue with
identifier
Then I deleted and remade the segue with no effect. So I changed the code back to this, which works but doesn't render the storyboard elements.
func clickView(forRowAtIndexPath indexPath: IndexPath) {
let myContents = UnitComponents[indexPath.row]
print("Clicked Report \(self.ProjectID) \(self.ShipListID) \(self.UnitName) \(myContents.sku)")
self.Sku = myContents.sku
let vc = ComponentViewController()
vc.Sku = myContents.sku
navigationController?.pushViewController(vc, animated: true)
}
Here is the vc2 code
import UIKit
class ComponentViewController: UIViewController {
var Sku = ""
var UnitName = ""
var ShipListID = ""
var ProjectID = ""
#IBOutlet var damageDesc: UITextView!
#IBOutlet var repairCost: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
print("Hello World \(Sku) \(UnitName) \(ShipListID) \(ProjectID)");
// Do any additional setup after loading the view.
}
#IBAction func saveReport(_ sender: Any) {
print("damageDesc \(String(describing: damageDesc ?? nil))")
print("repairCost \(String(describing: repairCost ?? nil))")
}
}
How can I fix my code so the storyboard layout etc gets loaded and I can see the form elements in the app?
Is this the wrong way to go to another view controller? I ask because it seems like some SO questions around this topic suggest it isn't calling the storyboard correctly. I'm searching for a swift 5 example of how to do this and only find references to instantiateViewControllerWithIdentifier("ViewController") which doesn't seem to be in swift 5
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = ShippingUnitsTableViewController()
vc.ShipListID = ShipLists[indexPath.row].ListID
vc.ProjectID = ProjectID
navigationController?.pushViewController(vc, animated: true)
}

you need to specify the storyboard in which the viewcontroller is located, and then instantiate the viewcontroller from that storyboard. Replace your func with the below one:-
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ShippingUnitsTableViewController") as! ShippingUnitsTableViewController
vc.ShipListID = ShipLists[indexPath.row].ListID
vc.ProjectID = ProjectID
navigationController?.pushViewController(vc, animated: true)
}
and make sure (inside the Main.storyboard file) you give ViewController's (which needs to be presented) Storyboard ID to be ShippingUnitsTableViewController in the attributes inspector (see the image below):-

can you try
func clickView(forRowAtIndexPath indexPath: IndexPath)
instead, didSelectRow of tableview delegate methods or view controller's performSegueWithIdentifier ?

Ok the solution to this problem required changing how I moved between view controllers. The didSelectRowAt indexPath method below does not utilize the storyboard, which causes the destination view controller to be sort of an orphan and unaware of the storyboard segues.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = ShippingUnitsTableViewController()
vc.ShipListID = ShipLists[indexPath.row].ListID
vc.ProjectID = ProjectID
navigationController?.pushViewController(vc, animated: true)
}
I changed didSelectRowAt indexPath to this, and added the bits of necessary info to the prepareSegue method as shown below.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.ListID = ShipLists[indexPath.row].ListID
performSegue(withIdentifier: "UnitsVC", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "UnitsVC"{
let nextViewController = segue.destination as? ShippingUnitsTableViewController
nextViewController!.ProjectID = self.ProjectID
nextViewController!.ShipListID = self.ListID
}
}

Related

Swift - Delegate function not returning value

VC-A is embedded in a nav controller and has a button going to VC-B via a popover segue. VC-B has a table view with a few font names. When I select a font name, VC-B closes and, using delegate/protocol, VC-A should get the selected name. It does not. I found that if I set a breakpoint at the end of didSelectRowAt, delegate is nil for some reason.
VC-A
class ViewController: UIViewController, FontDelegate {
#IBOutlet weak var infoLabel: UILabel!
let fontTable = FontTableTableViewController()
override func viewDidLoad() {
super.viewDidLoad()
fontTable.delegate = self
}
func getFontName(data: String) {
infoLabel.text = data
}
}
VC-B
protocol FontDelegate {
func getFontName(data: String)
}
class FontTableTableViewController: UITableViewController {
let fontArray = ["Helvetica", "Arial", "Monaco"]
var delegate: FontDelegate?
// MARK: - Table view functions go here...
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let font = fontArray[indexPath.row]
self.delegate?.getFontName(data: font)
self.dismiss(animated: true, completion: nil)
}
}
This shows the storyboard connection from the button in VC-A to VC-B.
Instead of linking Pick your font button to the other controller, I would suggest you creating an IBAction of the button to trigger the following code whenever pressed:
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = storyBoard.instantiateViewController(withIdentifier: "FontScreen") as? FontTableTableViewController else {return}
vc.delegate = self
vc.modalPresentationStyle = .fullScreen
self.navigationController?.pushViewController(vc, animated: true)
but for this to work you will need to go to the identity inspector of your FontTableTableViewController and from there you can name your controller in storyboardID field in Identity section as FontScreen.
This is not the way how to return a variable in a navigation controller Environment. I would like to suggest you use an unwind segue. Your code would then look like:
VC A
class ViewController: UIViewController {
var selectedFontName = ""
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func returnSelectedFont(sender: UIStoryboardSegue) {
print(selectedFontName)
}
}
The IBAction in VC A ist the method too which the tableview returns to if you use an unwind segue.
Your tableview controller then looks like this:
VC B
import UIKit
class FontTableTableViewController: UITableViewController {
var selectedFont = ""
let fontArray = ["Helvetica", "Arial", "Monaco"]
#IBOutlet var fontTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
fontTable.dataSource = self
fontTable.delegate = self
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return fontArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FontTable", for: indexPath) as! FontCell
cell.fontName.text = fontArray[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedFont = fontArray[indexPath.row]
performSegue(withIdentifier: "Return Fontname", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let returnVC = segue.destination as? ViewController {
returnVC.selectedFontName = selectedFont
}
}
}
To be able to get the return segue working you have to select the tableview cell and control drag it to the exit icon in the tableview controller. See therefore the picture.
After that you select the unwind segue and give it an identifier, to be used in VC B. See the code:
This should work perfectly.

Navigate from a ViewController to an another on didSelectRowAt programmatically

I have a UIViewController that contains a custom UITableView. The table has a custom UITableViewCell too.
How to navigate from the first ViewController to an another when you select/click one of the rows?
Note
I have not used StoryBoard.
Update
This my code. Each one of the classes are external file. Let me know, if you need more code.
class TestViewController: UIViewController, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(testTableView)
}
let testTableView: TestTableView = {
let table = TestTableView()
table.register(TestTableViewCell.self, forCellReuseIdentifier: TestTableViewCell.identifier)
return table
}()
}
class TestTableView: UITableView, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TestTableViewCell.identifier, for: indexPath)
return cell
}
}
class TestTableViewCell: UITableViewCell {
static let identifier = "testCell"
}
Here is a complete answer:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let newViewController = NewViewController()
self.navigationController?.pushViewController(newViewController, animated: true)
}
In UIKit in order to programmatically navigate from TableCell you can do it without storyboard.
You have two ways to view next viewController
If you want to present .......
animating from bottom to top
(one thing to remember) you can not present new view without dismissing previous presented screen, otherwise app crash due to conflict of presented screen
yourTableView.deselectRow(at: indexPath, animated: true)//used for single Tap
let vC = YourViewController (nibName: "" , bundle : nil)
vC.modalPresentationStyle = .fullScreen //this line is optional for fullscreen
self.present(vC, animated: true , completion: nil)
Or if you want to View your viewController normally (2 is batter) ....
animate from right to left
yourTableView.deselectRow(at: indexPath, animated: true)
let vC = YourViewController()
self.navigationController?.pushViewController(vC, animated: true)
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = yourCustomVCName (nibName: "yourCustomVC nibName" , bundle : nil)
self.present(vc, animated: true , completion: nil)
}

Sending Data to child view controller from parent view controller

I am trying to send data from parent view controller to child view controller, whenever I override the performSegue method, the data loads to the popup view as shown below:
But what I want is show data in something like picture shown below:
I have added the popup view to the main view from didSelectRowAt indexPath method, I used the protocol method, but it didn't load the data.
my code for parent view controller is shown below:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! TableViewCell
cell.customInit(noticeTitle: notificatonData[indexPath.row].notice_title,noticeDiscripetion: notificatonData[indexPath.row].notice_desc)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PopUpViewController")
self.addChildViewController(popOverVC)
popOverVC.view.frame = view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParentViewController: self)
performSegue(withIdentifier: "goToPopUpView", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PopUpViewController{
destination.notificationfianlData = notificatonData[(tableView.indexPathForSelectedRow?.row)!]
}
}
You should either use segue or child
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "goToPopUpView", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PopUpViewController{
destination.notificationfianlData = notificatonData[(tableView.indexPathForSelectedRow?.row)!]
}
select segue line and
select the main view in the popupVC and make it's background color transparent
Please try with this one. There should be no need of performSegue.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PopUpViewController")
self.view.addSubview(popOverVC.view)
popOverVC.notificationfianlData = notificatonData[indexPath.row]
popOverVC.view.frame = view.bounds
}
FYI. Make PopUpViewController View, background color transparent. It will show like this.

Delegate method to segue in tableviewcell

I have a delegate method where if I press a button in the tableview, it should segue to another view controller and pass along data but it doesn't seem to work.
func goToVC(uid: String) { //delegate method
performSegue(withIdentifier: "showVC", sender: self) //Do I need this
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showVC", sender: self)
self.tableView.deselectRow(at: indexPath, animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showVC" {
if let indexPath = tableView.indexPathForSelectedRow {
let guestVC = segue.destination as! GuestViewController
guestVC.ref = userArray[indexPath.row].ref
}
}
class MainViewController: UIViewController {
// set the cell's delegate in the data source
// pass the object to the cell from the data source
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell.mainViewControllerDelegate = self
cell.object = someArray[indexPath.row]
}
// this is the method that gets called by the cell through the delegate
func pushToViewController(object: YourDataObject) {
let destination = SomeViewController()
destination.object = object
navigationController?.pushViewController(destination, animated: true)
}
}
class TheTableViewCell: UITableViewCell {
// create a delegate and a data object
var mainViewControllerDelegate: MainViewController?
var object: YourDataObject?
// this is the method that gets called when the button in the cell is tapped
#objc func buttonAction() {
mainViewControllerDelegate?.pushToViewController(object: object)
}
}
I highly recommend that beginners do not use Interface Builder. The less you use it early, the quicker you will understand more. Interface Builder is fool's gold for beginners.
You dont need delegate method here. Delegate method can be used if you need to pass the value from the child view controller.
What you are doing is exactly right. Make sure you set the segue identifier in the story board correctly.
And one more thing dont set your table IBOutlet as default tableView try setting a name apt for that table like toDoTable, so it will easy to debug.

Send data from TableView to DetailView Swift

I'm trying to do maybe one of the simplest and more confusing things for me until now
I wanna develop my own App , and in order to do it I need to be able to passing some information depending of which row user click (it's Swift lenguage)
We have a RootViewController(table view) and a DetailViewController (with 1 label and 1 image)
(our view)
Here is the code:
#IBOutlet weak var tableView: UITableView!
var vehicleData : [String] = ["Ferrari 458" , "Lamborghini Murcielago" , "Bugatti Veyron", "Mercedes Benz Biome"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var nib = UINib(nibName: "TableViewCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return vehicleData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:TableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as TableViewCell
cell.lblCarName.text = vehicleData[indexPath.row]
cell.imgCar.image = UIImage(named: vehicleData[indexPath.row])
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("DetailView", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "DetailView") {
var vc = segue.destinationViewController as DetailViewController
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
Custom TableViewCell class (has a xib File with cell)
class TableViewCell: UITableViewCell {
#IBOutlet weak var lblCarName: UILabel!
#IBOutlet weak var imgCar: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
class DetailViewController: UIViewController {
#IBOutlet weak var lblDetail: UILabel!
#IBOutlet weak var imgDetail: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
The question is:
if user click Ferrari 458 , the lblDetail in DetailViewController would show: Ferrari 458 is a super car which is able to reach 325 km/ h ...... (whatever we want)
and imgDetail would be able to show an image (whatever we want) of the car
If user click Bugatti Veyron now the lblDetail show us: Bugatti Veyron is a perfect and super sport machine. It's one of the fastest car in the world....
imgDetail show us an image of this car
Same thing with all cars depending which row we have clicked
I know the work is around prepareForSegue func in first View Controller but i was trying a lot of different ways to make it possible and anything runs ok
How we can do this???
Here is the example for you:
var valueToPass:String!
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRowAtIndexPath(indexPath)! as UITableViewCell
valueToPass = currentCell.textLabel.text
performSegueWithIdentifier("yourSegueIdentifer", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "yourSegueIdentifer") {
// initialize new view controller and cast it as your view controller
var viewController = segue.destinationViewController as AnotherViewController
// your new view controller should have property that will store passed value
viewController.passedValue = valueToPass
}
}
But don't forget to create a passedValue variable into your DetailViewController.
This is just an example of passing data from one viewController to another and you can pass data with this example as you need.
And for more info refer this links.
Passing values between ViewControllers based on list selection in Swift
Use didSelectRowAtIndexPath or prepareForSegue method for UITableView?
Swift: Pass UITableViewCell label to new ViewController
https://teamtreehouse.com/forum/help-swift-segue-with-variables-is-not-working
May be this will help you.
Swift 3.0
var valueToPass:String!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
valueToPass = currentCell.textLabel?.text
performSegue(withIdentifier: "yourSegueIdentifer", sender: self)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "yourSegueIdentifer") {
// initialize new view controller and cast it as your view controller
var viewController = segue.destination as! AnotherViewController
// your new view controller should have property that will store passed value
viewController.passedValue = valueToPass
}
}
This may be another solution, without much code in didSelectRowAtIndexPath method.
Note that while it may look cleaner, and we do not need an extra variable valueToPass, it may not be a best practice, because the sender argument inside performSegue method is supposed to be the actual object that initiated the segue (or nil).
// MARK: UITableViewDelegate methods
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "goToSecondVC", sender: indexPath)
}
// MARK: UIViewController methods
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToSecondVC" {
if segue.destination.isKind(of: CarDetailsController.self) {
let secondVC = segue.destination as! CarDetailsController
let indexPath = sender as! IndexPath
secondVC.passedValue = carsArray[indexPath.row]
}
}
}
If you drag a segue from the prototype cell (in the Interface Builder) to your next View Controller and set its segue identifier to "Your Segue Identifier", you can also do it with this shortcut:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Your Segue Identifier" {
let cell = sender as! YourCustomCell
let vc = segue.destination as! PushedViewController
vc.valueToPass = cell.textLabel?.text // or custom label
}
}
And you also don't need the performSegueWithIdentifier() in the didSelectRowAtIndexPath(), nor this Table View method.
In PushedViewController.swift (the next View Controller):
var valueToPass: String!
override func viewDidLoad() {
super.viewDidLoad()
yourLabel.text = valueToPass
}
It's important to set the label's value after it initialized from the Storyboard. That means, you can't set the label in the previous View Controller's prepareForSegue() directly, therefore needing to pass it with valueToPass.
Its simple, am adding one statement to above answer.
To get the selected car name in detail view label,
lblDetail.text = passedValue
you can add this code of line in viewDidLoad() func of your detailed view. passedValue contains the name of car which user selected(assign in prepareForSegue) then you can assign to your detailedView label.
Hope it helps!!