How to get the right row in a #IBsegueAction function? - uikit

I can segue from an embedded UIKit tableview to a SwiftUI view, with the necessary data. I select the indexPath.row with a tableView(_didSelectRowAt).
However, the #IBSegueAction takes place before the didSelectRowAt. This makes the detailView lag one selected row: it shows the previously selected row.
I tried to put the didSelectRowAt first, tried to embed them: no chance
I saw in a WWDC video that it should be possible lo select the right row, but can't figure out the right syntax from this short segment (about minute 6:00)
https://developer.apple.com/videos/play/wwdc2019/231/
#IBSegueAction func MeasurementDetail(_ coder: NSCoder) -> UIViewController? {
return UIHostingController(coder: coder, rootView: PointDetailSwiftUIView(pointDetail: measurements[selectedMeasurementRow]))
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedMeasurementRow = indexPath.row
}
How do I solve the problem?

In that WWDC session, Tanu doesn't care when UIKit calls tableView(_:didSelectRowAt:). Instead, her #IBSegueAction asks the table view for the selected row, by using tableView.indexPathForSelectedRow.
Make sure you have a tableView outlet connected to your table view. You already have a tableView outlet if your view controller is a subclass of UITableViewController. Then use tableView.indexPathForSelectedRow:
#IBSegueAction func MeasurementDetail(_ coder: NSCoder) -> UIViewController? {
guard let row = tableView.indexPathForSelectedRow?.row else { return nil }
let detailView = PointDetailSwiftUIView(pointDetail: measurements[row])
return UIHostingController(coder: coder, rootView: detailView)
}

Related

Create a custom list on Storyboard Xcode

Being a beginner with Xcode, I wanted to know if it was possible to create a list with our own components? I explain, it would be to make a list of favorites scrolling on an Xcode view, with an image, a button to delete it, and a text. It is obvious that I will have to create more components but how to put them in a list without limits?
The prototype design is as follows:
Here !
Thank you in advance, Sincerely.
Yes you can make your own list cells. For that you will need to,
create your own UITableViewCell and add your componenents (2x UIImageViews, 1x UILabel)
register your cell to the UITableView you want to display the cell in with the identifier
Conform to both UITableViewDelegate & UITableViewDataSource
Refer this blog to learn more as I barely scratched the surface with the explanation
You can add data to the table as follows
/// Conforming to the UITableViewDelegate, UITableViewDataSource protocols
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Table view to display data
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// setting the datasource and delegate
tableview.delegate = self
tableview.dataSource = self
}
/// 2d Array to store data
let data = [["Melon", "Glace"], ["Epinard"], ["one", "two", "three"] ]
func numberOfSections(in tableView: UITableView) -> Int {
return self.data.count
}
/// UITableViewDataSource protocol stubs
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.data[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell") as? CustomTableViewCell {
cell.name = data[indexPath.section][indexPath.row]
return cell
}
return UITableViewCell()
}
}

How to use two tableview in one view?

I have two table view in one view container. I insert each of them to the container.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
self.storyboard?.instantiateViewController(withIdentifier: "ChildTableView") as! ChildTableViewController
childTV.updateTable(id: indexPath.row)
}
I need to update second tableview when first tableview cell clicked.
How i can do this?
Okay, I think I see the problem. The way you've done this is with two UITableViewControllers and their table views. So now you have a problem communicating between the two table view controllers. But consider this: if you've truly created child view controllers, the table view controllers are children of the same parent. So the first table view controller can get its parent view controller, then get the parent's child view controllers, then get the second child view controller, and that must be the second table view controller.
In other words, if your view controller hierarchy is:
ParentViewController
TableViewController1
TableViewController2
... then TableViewController1 can say:
let tvc2 = self.parent!.childViewControllers[1] as! TableViewController2
... and now you have established a line of communication between them. So now table view controller 1 can talk to table view controller 2 and tell it to update its table. You will need to have a method in table view controller 2 that table view controller 1 can call, in order to hand it any needed data and tell it what to do.
in Second TableView:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ChildTableViewController.updateTable(sender:)), name: NSNotification.Name(rawValue: "updateChild"), object: nil)
}
func updateTable(sender:NSNotification){
tableView.reloadData()
}
in First TableView:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
selectedItem["id"]=indexPath.row
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateChild"),object:selectedItem)
}

didSelectRowAtIndexPath and performSegueWithIdentifier

I have a tableView that display a list of cells. I need to catch the selected cell, and pass my object data based on indexPath.row to display it on my Second View Controller. My didSelectRowAtIndexPath is working fine, but probably I'm not familiar with the syntax for performSegueWithIdentifier, where it fails to call the method.
Sorry I'm new to Swift programming, would appreciate if you could explain more details.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
selectedCell = indexPath.row
performSegueWithIdentifier("DetailScreen", sender: self)
}
override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) {
detailedView.personName = personList[selectedCell].GetPersonName()
}
Solution 1
First, make sure you have segue between your first and second view controllers (From the table view controller one to the Detail view screen). Make sure that the segue has a name
Now in your first view controller's didSelectRowAtIndexPath event, Invoke the segue like this.
performSegueWithIdentifier("DetailScreen", sender: nil)
Now in the prepareForSegue method of the first view controller, you can customize it to send more details.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if(segue.identifier=="DetailScreen")
{
let ctrl = segue.destinationViewController as! YourDetailsViewController
ctrl.personName = "Set some person name here"
}
}
Assuming Your Detail screen's class is called YourDetailsViewController and it has a property called personName of String type. You may update the code to use your real view controller.
Solution 2
If you do not want to create the segue, you can programmatically navigate( actually pushing the second view controller to the front) to the second view. In your first view controllers, didSelectIndexPathAtRow method,
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let ctrl = self.storyboard?.instantiateViewControllerWithIdentifier("DetailsCtrl")
as? YourDetailsViewController
ctrl?.personName="Set some name here"
self.navigationController?.pushViewController(ctrl!, animated: true)
}
For the above code to work, you need to make sure that you have StoryBoardID set to "DetailsCtrl" on the second view controller(Details view)
You have set the storyboard segue as given below:
Click on the segue and add the identifier
You can achieve the above requirement using the following code and this worked for me:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let storyboard1 = UIStoryboard(name: "Main", bundle: nil)
let detail = storyboard1.instantiateViewControllerWithIdentifier("DetailScreen") as! SecondViewController
detail.personName = personList[selectedCell].GetPersonName()
self.navigationController?.pushViewController(detail, animated: true)
}
The storyboard segue from your first view controller to second view controller should be "Push".
Hope this might be helpful.

Passing an image when taped on uicollectionview to a detail view controller in swift

Ive been at this for a while but cant seem to crack it in swift
I want a user to be able to select an image in uicollectionView and for that image to appear in a detailed view controller, i can do this quite easily with a peice of text,
and i can do this when there is a static array of images preloaded. but i cant seem to get anywhere with a collectionview which is loaded with images from a camera.
I understand i need to use
override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) {
}
and this function to isolated selected cell.
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
}
I do have these outlets
#IBOutlet weak var collectionView: UICollectionView!
var images = [UIImage]()
image picker stores all images to this array by
images.insert(newImage, atIndex: 0)
when the array would be passed to the detailviewcontroller, i understand that would have to be copied into another local array and then how would i get the current image that was highlighted to be shown first, perhaps using indexPath.Row
Regards
I'm not using segues, and actually I don't quite understand what your problem is, but I'll try to show you how it could be achieved.
First of all, you have an array with images, so I believe your image should be accessed as images[indexPath.row]
let's suppose that you already have UIViewController class with UIImageView in it.
if so, you can write something like that:
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
let myVC = MyViewController()
myVC.imageView.image = images[indexPath.row]
self.presentViewController(myVC, animated: true, completion: nil)
}
for modal or
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
let myVC = MyViewController()
myVC.imageView.image = images[indexPath.row]
self.navigationController?.pushViewController(myVC, animated: true)
}
if you want to show it as navigational.
I believe that with segues it's basically the same but I think you have to use func prepareForSegue for, you know, preparing the segue (func performSegueWithIdentifier will just perform it). In prepareForSegue you should check identifier of your segue
if segue.identifier == "myIdentifier" {
//your code here
}
and if this identifier is right, put your commands to your myVC there.

Xcode beginner having run time error on simple tableview code

I am learning how to use a table based app using Simon NG Swift Programming guide. I typed in the code verbatim and the Xcode environment gets stuck on the let cell = tableView line of code.
Can someone tell me what I am doing wrong?
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var restaurantNames = ["Cafe Deadend", "homei", "teakha", "cafe loius", "petite oyster", "royal oak", "for knee rest",
"jimmy johns", "mickey dee", "daddies big burgers"]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return restaurantNames.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as UITableViewCell
// configure the cell
cell.textLabel?.text = restaurantNames[indexPath.row]
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The first error I saw when running your code is this:
2015-02-17 16:28:05.645 delete-me-maps[8008:151860] *** Terminating app due
to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to
dequeue a cell with identifier Cell - must register a nib or a class for the
identifier or connect a prototype cell in a storyboard'
If this is the error you're getting, then you need to add the following to viewDidLoad:
if let myTableView = self.tableView {
self.tableView!.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
Otherwise dequeueReusableCellWithIdentifier does not know what kind of class to use for the cell.
Could you post the error message?
Also, wether the app builds or fails building is not only about the code in your .swift files, it's also about the correct tagging/identifying in your storyboard. Have you made sure you haven't messed up the cell identifiers on your project earlier?
You may simply need to set the prototype cell identifier to "Cell". (Make sure it is exactly the same as in the code).
Go to the storyboard, click on your tableview, click on the attributes.
Give yourself a prototype cell by changing it from 0 to 1.
Click on the prototype cell. Set the 'identifier' attribute to "cell".
I believe that you need to set the Prototype Cell to have the identifier "Cell". You do this by:
Going to Main.storyboard, clicking the cell in the Document Outline, then go to the Attributes Inspector and Type in Cell in the 'Identifier' field.

Resources