Passing a Struct Array through a TableViewCell to another ViewController - ios

I'm new to swift and I've been stuck on this for a while now I'm trying to pass a Struct Array from a tableview cell to another view

If you want to share specific cell's data into table of other view controller, then you need to create an object of Model struct rather than its array just like below:
var newFeed: Model?
Next you can assign it value of particular cell before navigation in main did select method as below:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let vc = storyboard?.instantiateViewController(withIdentifier: "Comment") as? Comment else {return}
vc.newFeed = getInfo[indexPath.row]
navigationController?.pushViewController(vc, animated: true)
}
It will assign that cell's data to newFeed Variable and relaod table with that data.
For any question, feel free to ask.

Related

How can I pass data from "didSelectRowAt" to another VC without presenting it or a segue?

What I basically want is I want to press on a row in my TableViewController which then takes, for example, the text that the row has and passes it to a ViewController that is currently not present. Like when you click on a song in the Spotify app and it plays it without presenting anything but the song details are shown in the mini-player.
Does anyone have a clue how to do that?
Well you can do that but you need to initialize your View Controller first for you to access its properties like:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DestinationVc") as? DestinationVc
vc?.text = "TextToBePassed"
}
And there it is without presenting it but I don't get it why you don't want to present or show it but that's how it is done based on your question. Thanks :)
If you have created the UI via XIB or programatically then follow this approach:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let nextVC = NextViewController()
nextVC.text = model?[indexPath.row].text // If you are using model or if you have stored the data in the array then nextVC.text = yourArray[indexPath.row]["text"]
self.navigationController?.pushViewController(nextVC,animated: true)
}
Also, in the NextViewController, you have to add text. Like this:
class NextViewController: UIViewController {
var text = String()
//MARK:- View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
print(text) //This will print the text passed from previous VC
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = NextViewController()
vc.title = "\(Title)"
self.navigationController?.pushViewController(vc,animated: true)
}
Using this method can help you in passing the data to another Controller without using segue.
Assuming this is for iOS as the question doesn't specify.
In the tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) method in your UITableViewDelegate, you can grab the cell's text with
let cell = tableView.cellForRow(at: indexPath)?.textLabel?.text // cell: String?
You can then assign this to a global variable if you wish, or if you maintain a reference to the non-present view controller, you can give it a property to store this text and assign it before you call performSegue(withIdentifier identifier: String, sender: Any?).

Swift looping through array

I have a UITableView that has 100 cells. I want to create an array that will hold index values of that table that I want to disable the table cells if the table row selected matches any of the values in the array.
I have found that the following code works to disable a specific cell that I give it.
UITableViewCellSelectionStyle.none
Any help on this would be greatly appreciated.
This is how I am checking which cell is selected:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedRow = tableView.indexPathForSelectedRow?.row
let workoutSelected = selectedRow
stringPassedTableView = workoutSelected!
let myVC = storyboard?.instantiateViewController(withIdentifier: "showWorkout") as! WorkoutViewController
myVC.stringPassed = stringPassedTableView
navigationController?.pushViewController(myVC, animated: true)
}
You can use the following tableview delegate method to allow selection or not
tableView:willSelectRowAtIndexPath:
Simply check if the cell index is in your disabled cell indexes and if so return nil. Otherwise return the indexpath.
You can see the documentation here:
https://developer.apple.com/documentation/uikit/uitableviewdelegate/1614943-tableview?language=objc#return-value
A much better way that, create an array of your model class which data you are passing into tableView data source.
Add a property into model class like named as "isSelectable" and assign it properly when you are parsing your model class data.
Use this "isSelectable" property, when you will select a row then check this property it is true or false and performs operation accordingly.
Hope you understand !!
You have an array (or set) as a class property
var disabledRows = [IndexPaath]()
then simply use it at the start of the func
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if disabledRows.contains(indexPath) { return }
// code to handle row with enabled cell
}

Passing Data Forward In Swift Without Segue

I'm currently making a to do list app using Swift 4. The home view controller has a tableview with some categories in it and when one is selected, it takes the user to a view controller where the items in that category are listed. I have a bug however as only the most recent item is showing in the list.
I think this is due to the way I am navigating to the list view controller. I am currently doing this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let destinationVC = ListVC()
if let indexPath = tableView.indexPathForSelectedRow {
destinationVC.selectedCategory = categoryArray[indexPath.row]
}
navigationController?.pushViewController(destinationVC, animated: true)
tableView.deselectRow(at: indexPath, animated: true)
}
And in the list view controller, I just have this to load the data:
var selectedCategory : Category? {
didSet {
loadItems()
}
}
I firstly created this app using storyboards and when using segues, it worked completely fine.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "goToItems", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! TodoListVC
if let indexPath = tableView.indexPathForSelectedRow {
destinationVC.selectedCategory = categoryArray[indexPath.row]
}
}
So basically, the problem is that in the secondary list view controller, it will only show the most recently added item and no other ones even when they are stored in core data. I think it is to do with the way I am showing the secondary view controller as I am creating a new object every time.
How to properly go to the next view controller?
Remove the segue and add the storyboard id
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "storyboard_id") as! TodoListVC
vc.selectedCategory = categoryArray[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
Try this it will help you:-
You can send data from one view controller to another using storyboard
instance.
let next = self.storyboard?.instantiateViewController(withIdentifier: "NextControllerStoryBoard_id")as! NextController
next.str = "data which you want to pass"
self.navigationController?.pushViewController(next, animated: true)
here NextController is your controller class name where you want to go.str is the string name which you declare on NextController like
let str = String()
you are able to send string in that variable in same way you send any thing array dictionary ,image, Int value etc.
NextControllerStoryBoard_id is id which you declare at storyboard of that controller
In storybard id add your storybard id
Hope this will help you
I think that with this chunk of code I already sensed that you are passing data to the other view controller the incorrect way:
let destinationVC = ListVC()
if let indexPath = tableView.indexPathForSelectedRow {
destinationVC.selectedCategory = categoryArray[indexPath.row]
}
...
What I would suggest is that, instead of passing the data this way, you have to pass an array containing the items within the selected category using an array, then pass that array via the prepare for segue.
Then from the viewdidappear or viewdidload method in the receiving view controller, use the passed array from the source VC and use that as a datasource for your table view within that 2nd VC.

Swift: How to get the table row cell (indexPath) and load it from the data model in detail view controller?

It took me while to get the core data model working and I can create content in a detail view controller and pass it to my table view to display the newly created row in the table, but I cannot find out how to correctly display the data in the detail view controller again.
Since I am using core data, I was thinking about just getting the index path for the selected table view row and pass it to the detail view controller, where the appropriate data would be loaded from the data model array, but how to pass the indexPath to the detail view controller and load the data?
There is only one barButtonItem in my master view to add new content - plus the tableView.
In my tableView I fetch and display the data like this:
var managedObjectContext: NSManagedObjectContext!
var coasters = [Coaster]()
override func viewDidLoad() {
super.viewDidLoad()
// Add NoteficationCenter observer to reload table from another view.
NotificationCenter.default.addObserver(self, selector: #selector(loadData), name: NSNotification.Name(rawValue: "reload"), object: nil)
// Initialize Core Data ManagedObjectContext
managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
loadData()
}
#objc func loadData() {
let coasterRequest: NSFetchRequest<Coaster> = Coaster.fetchRequest()
// Sorting from newest to oldest.
let sortDescriptors = NSSortDescriptor(key: "date", ascending: false)
coasterRequest.sortDescriptors = [sortDescriptors]
// Attempt to load data from Core Data!
do {
coasters = try managedObjectContext.fetch(coasterRequest)
self.tableView.reloadData()
} catch {
print("loading error: \(error.localizedDescription)")
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CoasterTableViewCell
let coasterItem = coasters[indexPath.row]
if let coasterImage = UIImage(data: coasterItem.image as! Data) {
cell.bacgkgroundImageView.image = coasterImage
}
cell.nameLabel.text = coasterItem.name
cell.parkLabel.text = coasterItem.park
cell.dateLabel.text = coasterItem.date
cell.countLabel.text = String(coasterItem.counter)
return cell
}
To explain this: In the detail view controller everything is saved to core data when the user hits "save" and returns to master view. I added a simple Notification Center observer which is triggered from the detail view controller and tells the master view to reload the data (that was the easiest for me to reload everything without using the prepare function. And it works perfectly.
Now to the problem:
So when the user clicks on a table view cell the indexPath should be passed to the detail view and there it should load the correct data from the data model (obviously I guess I have to check whether the user wants to add something or just views the content). How can this be done?
I hope this is all you need, if I should add something just ask.
try like this
extension MasterViewController: UITableViewDelegate{
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//here 'self.tableDataSourceArray' is the data set fetched from the database
let selectedObject = self.tableDataSourceArray[indexPath.row]
// pass 'selectedObject' to the detail viewcontroller
let detail = ......
detail.currentObjectToDisplay = selectedObject
//in case, if you are using same view controller to add new info and show the existing info, you are required to take one more property in the detail viewcontroller to handle the data
detail.addMode = false //'true' if you are adding
}
}

Send information to another View controller

I am having trouble getting the Indexpath of table view cell and sending it to a next page.
var bookName: String?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cells", for: indexPath) as! ProfileTableViewCell
print(posts[indexPath.row])
let post = self.posts[indexPath.row] as! [String: AnyObject]
self.bookName = post["title"] as? String
}
override public func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "sendMessageToUser", let chatVc = segue.destination as? SendMessageViewController else {
return
}
chatVc.bookName = self.bookName
}
So I am trying to capture the title of whatever cell I clicked and send it to SendMessageViewController. The issue is that it captures some titles accurately and sometimes it does not capture the titles accurately, and I am not sure why.
You need to implement the table view delegate method tableView(_:didSelectRowAt:). In that method, save the selected indexPath to an instance variable and invoke your segue.
Then in prepareForSegue, use the saved indexPath to index into your data model and fetch the data for the selected cell. (The title string, in your case.) Don't fetch the data from the cell's views. Views are not for storing data.
cellForRowAt method serves to create view, in this case a table view cell, and then provide that to table view to display. When table view loads data, you will see, say 10 cells. So this function is called 10 times to prepare the 10 cells. So in the last time, the index row will be 9 and your bookName property will be the 10th of your post array.
Now say you scroll down a bit and then scroll all the way up, the last cell getting prepared is the first cell. So now your bookName will be the first of your post array. That's why you are getting incorrect book name.
To fix your problem, you need to get the specific book name only after user clicked on a cell. So remove the code that assign values to bookName in your cellForRow method, and then add another delegate function like this
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let post = self.posts[indexPath.row] as! [String: AnyObject]
self.bookName = post["title"] as? String
}

Resources