Move Text from cell to text label in a different view controller - ios

I have created an app that displays 10 random numbers in a table view. This is the code I used
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ShowNumber", for: indexPath)
// Configure the cell...
cell.textLabel?.text = "\(indexPath.row + 1): \(Int(arc4random_uniform(10001)))"
return cell
}
Now I want to be able to click on one cell and be taken to another view controller and in the center of the page it displays the random number. I have the view controller all set up and linked to the original table view. I'm just having trouble passing my data through the segue. This is what I have so far. I know it unfinished, I just don't know what to do.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showRandomNumber" {
let controller = segue.destination as! ShowNumberTableViewController
let selectedRow = (tableView.indexPathForSelectedRow as NSIndexPath?)?.row ?? 0
controller.LabelText =
}
The LabelText is the name of the label that I want to pass the data to, but I don't know how to

Using the indexPathForSelectedRow, you can get a reference to the cell that triggered the segue. You can then access the textLabel within that cell, get its value, and pass it to the next view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showRandomNumber" {
let controller = segue.destination as! ShowNumberTableViewController
if let indexPath = tableView.indexPathForSelectedRow {
let cell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell
if let textToPass = cell.textLabel.text {
controller.labelText = textToPass
}
}
}
}
Then in ShowNumberTableViewController you want a var labelText and in your viewDidLoad() you can assign it to the textLabel in that view controller with
centerLabel.text = labelText

The way you are doing, the numbers will change when you scroll the table view. I think you should save the numbers in an array.
About how to grab the selected number in the prepareForSegue: I would do this way:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showRandomNumber" {
if let cell = sender as? UITableViewCell, indexPath = tableView.indexPathForCell(cell) {
let selectedNumber = numbers[indexPath.row]
controller.labelText = String(selectedNumber)
}
}
}

Related

swift tableview sentence bug

I am trying to make a tableview where the user can click on the cells so the cells open a new ViewController which sets the navbar title to the clicked cell's label.
It doesn't really work for me because when I click on a cell it opens a view which has the previously clicked cell's value (not the current cells value) and then again opens a new view where the title is the current cells's value.
I uploaded the viewcontrollers to a GitHub repo: https://github.com/SiposPtr/stackoverflow
var selectedValue: String?
var numberOfFileToLoad: Int = 1
let cellak = [
"Első",
"Második",
"Harmadik",
"Negyedik",
"Ötödik",
"Hatodik",
"Hetedik",
"Nyolcadik",
"Kilencedik"
]
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return cellak.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
cell.textLabel?.text = cellak[indexPath.row]
// Configure the cell...
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedValue = cellak[indexPath.row]
numberOfFileToLoad = indexPath.row + 1
performSegue(withIdentifier: "gotoSentences", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "gotoSentences"{
let nextViewController = segue.destination as! ButtonEditViewController
nextViewController.title = selectedValue
nextViewController.numberOfFileToLoad = numberOfFileToLoad
}
}
It seems the segue is performed twice. If you have a segue connection from a table view cell to next view controller, the segue performed immediately after the cell is selected and didSelectRowAt method will be called after that.
You can get the selected row in prepare for segue method and pass the selected data to next view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "gotoSentences"{
if let nextViewController = segue.destination as? ButtonEditViewController,
let indexPath = tableView.indexPathForSelectedRow {
nextViewController.title = cellak[indexPath.row]
nextViewController.numberOfFileToLoad = indexPath.row + 1
}
}
}

a simple performSegue throws and error in prepare for segue func

Maybe something silly, but I am trying to simply move from a tableviewController to a viewController not passing any data just a simple move using
#IBAction func requestPanel(_ sender: Any) {
performSegue(withIdentifier: "sendMail", sender: AnyObject)
}
And then I get an error in a prepare for segue func.
But what does that have to no with my other segue?
I understand that that segue will have trouble since there is no data.
As you see here segue is above prepare for segue.
#IBAction func requestPanel(_ sender: Any) {
self.performSegue(withIdentifier: "sendMail", sender: AnyObject.self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var indexPath: IndexPath = self.tableView.indexPathForSelectedRow!
let desti = segue.destination as! DeviceDetailTableViewController
let selectedRecord = onlineDevices[indexPath.row]
let panelWidth = selectedRecord.object(forKey: "panelwidth") as? String
let panelHight = selectedRecord.object(forKey: "panelhight") as? String
let panelPitch = selectedRecord.object(forKey: "panelpitch") as? String
let panelPower = selectedRecord.object(forKey: "panelpower") as? String
let panelWeight = selectedRecord.object(forKey: "panelweight") as? String
let panelMaker = selectedRecord.object(forKey: "maker") as? String
let panelModel = selectedRecord.object(forKey: "model") as? String
desti.pawidth = panelWidth!
desti.pahight = panelHight!
desti.papitch = panelPitch!
desti.papower = panelPower!
desti.weight = panelWeight!
desti.maker = panelMaker!
desti.model = panelModel!
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return onlineDevices.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DeviceCell", for: indexPath)
// Configure the cell...
let noteRecord: CKRecord = onlineDevices[(indexPath as IndexPath).row]
cell.textLabel?.text = noteRecord.value(forKey: "maker") as? String
cell.detailTextLabel?.text = noteRecord.value(forKey: "model") as? String
return cell
}
}
All segues in a viewController will go through the same prepare(for:sender:) method. You need to use the segue.identifier to handle the different segues differently.
Your sendMail segue comes from a UIBarButton action instead of from a selected row, so check for that segue and handle it separately:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "sendMail" {
// nothing extra to be done here
} else {
// do your normal path here
if let indexPath = self.tableView.indexPathForSelectedRow {
// you have a valid selected row so proceed
}
}
}
The error message is clear. You are saying tableView.indexPathForSelectedRow at a time when no row is selected. Therefore that value is nil, and you crash when you force-unwrap it.
You wrote your original prepareForSegue when your only segue was triggered by the user selecting a row of the table. But this segue is not triggered by the user selecting a row of the table; it is triggered by the user tapping a button. You have to account for that difference.
(This is a major flaw in the whole architecture: all segues from a given view controller flow into the same prepareForSegue, which becomes a bottleneck.)

How to change button title from table static cells?

I have HomeViewController that's segued modally to a Navigation Controller with an identifier of: pickSubjectAction
And on SubjectPickerTableViewController is where my subjects to choose. This is my code
import UIKit
class SubjectPickerTableViewController: UITableViewController {
var subjects:[String] = [
"English",
"Math",
"Science",
"Geology",
"Physics",
"History"]
var selectedSubject:String? {
didSet {
if let subject = selectedSubject {
selectedSubjectIndex = subjects.index(of: subject)!
}
}
}
var selectedSubjectIndex:Int?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return subjects.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SubCell", for: indexPath)
cell.textLabel?.text = subjects[indexPath.row]
if indexPath.row == selectedSubjectIndex {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
//Other row is selected - need to deselect it
if let index = selectedSubjectIndex {
let cell = tableView.cellForRow(at: IndexPath(row: index, section: 0))
cell?.accessoryType = .none
}
selectedSubject = subjects[indexPath.row]
//update the checkmark for the current row
let cell = tableView.cellForRow(at: indexPath)
cell?.accessoryType = .checkmark
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "saveSelectedSubject" {
if let cell = sender as? UITableViewCell {
let indexPath = tableView.indexPath(for: cell)
if let index = indexPath?.row {
selectedSubject = subjects[index]
}
}
}
}
}
This is segued also with identifier: savedSelectedSubject.
Q1: How can i segued from button to the tableview controller?
I tried this but failed
Q2: How to changed the button titled from selected Subject?
my resources: https://www.raywenderlich.com/113394/storyboards-tutorial-in-ios-9-part-2
Any help is appreciated. Thanks
How can i segued from button to the tableview controller? I tried
this but failed
performSegueWithIdentifier expects a String format while you are sending it in Any format. Correct way for call this function is
self.performSegue(withIdentifier: "pickSubjectAction", sender: self)
How to changed the button titled from selected Subject?
I guess you want to reflect the selected subject in the controller presented after savedSelectedSubject segue. Let ViewController A be the one through which you are pushing/presenting and ViewController B be the one which is pushed/presented. Follow the following steps :
For this you need to fetch the destination controller (B) from prepare(for segue: UIStoryboardSegue, sender: Any?) function via segue.destination property.
Make a public variable in B in which you can set your selected subject.
Using that property you can show your selected subject title.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let B = segue.destination
// Assuming B's public var is named selectedSubject and is of String type
if let subject = self.selectedSubject {
B.selectedSubject = subject
}
}
In Controller B
override func viewWillAppear() {
super.viewWillAppear()
button.setTitle(self.selectedSubject, for: .normal)
}
// We already have the value of selectedSubject which we are using to set title for the button.

TableViewCell to ViewController - Swift

I tried many ways but still can't go to another ViewController. This is my storyboard.
and This is my code.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? HistoryMainTableViewCell {
listItems.sort() { $0.seq > $1.seq }
let history = listItems[indexPath.row]
cell.setValue(history: history)
return cell
} else {
return HistoryMainTableViewCell()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
if let productHistoryPage = segue.destination as? HistoryProductViewController {
if let IndexPath = tableViewHistory.indexPathForSelectedRow {
let historyArray = listItems[IndexPath.row]
productHistoryPage.history = historyArray
}
}
}
}
Try this code:(Untested Code)
Note: Below code will trigger prepare segue. When, tableViewCell selected.Let me know the code works...
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
// let indexPath = myTableView!.indexPathForSelectedRow
// let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
//sendSelectedData = (currentCell.textLabel?.text)! as String as NSString
performSegue(withIdentifier: "vcProductHistory", sender: self)
}
Based on your answer to my initial comment: dragging the line from initial vc to destination vc tells your vc about the segue, preparing for segue tells your vc what do when the segue is triggered, but you still need to let your vc know when it should perform the segue. try this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? HistoryMainTableViewCell else { return HistoryMainTableViewCell() }
let history = listItems[indexPath.row]
cell.setValue(history: history)
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
guard let productHistoryPage = segue.destination as? HistoryProductViewController, let indexPath = sender as? IndexPath else { return }
let historyArray = listItems[indexPath.row]
productHistoryPage.history = historyArray
}
}
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
performSegue(withIdentifier: "vcProductHistory", sender: indexPath)
}
I think this is a little bit neater using the guard syntax. Also, it uses sender as the indexPath, not using tableView.indexPathForSelectedRow.
Also, I noticed you have this in your cellForRowAtIndexPath:
listItems.sort() { $0.seq > $1.seq }
Your sorting your data source in cellForRowAtIndexPath. You can't do that there because that function gets called for each row individually. You need to do the sorting before that is called, somewhere like viewWillAppear. Also, to sort an array in place you need to remove the () after sort. try this.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
listItems.sort { $0.0.seq > $0.1.seq }
tableView.reloadData()
}
I think you should connect to UINavigationController , not to HistoryProductionViewController
Because your MainViewController's segue connected to Navigation Controller
I guess in func prepareForSegue segue.identifier == "vcProductHistory" it works but, if let productHistoryPage = segue.destination as? HistoryProductViewController not works
because your destination ViewController's class is NavigationController
you should call
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
if let naviVC = segue.destination as? UINavigationController {
if let productHistoryPage = naviVC.topViewController as? HistoryProductViewController {
if let IndexPath = tableViewHistory.indexPathForSelectedRow {
let historyArray = listItems[IndexPath.row]
productHistoryPage.history = historyArray
}
}
}
}
}
Your solution - as you described it - should work.
The most work here can be done directly in the Storyboard.
Select the prototype cell in the TableView, drag the Segue to the NavigationController and select presentModally.
This should work without further magic.
When selecting the TableViewCell the NavigationController should be presented. There is also no code needed at this point (except for the population of the TableView).
If not - you maybe misconfigured your TableView by setting the selection to No Selection:
or you set User Interaction Enabled to false somewhere.

Swift 2.1 - How to pass index path row of collectionView cell to segue

From the main controller that I have integrated collection view, I want to pass selected cell index path to another view controller (detail view)
so I can use it for updating a specific record.
I have the following working prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "RecipeDetailVC" {
let detailVC = segue.destinationViewController as? RecipeDetailVC
if let recipeCell = sender as? Recipe {
detailVC!.recipe = recipeCell
}
}
}
And I've tried including let indexPath = collection.indexPathForCell(sender as! UICollectionViewCell) but I get Could not cast value of type 'xxx.Recipe' (0x7fae7580c950) to 'UICollectionViewCell' at runtime.
I also have performSegueWithIdentifier("RecipeDetailVC", sender: recipeCell) and I wonder if I can use this to pass the selected cell's index path but not sure I can add this index to the sender.
I am not clear about the hierarchy of your collectionViewCell. But I think the sender maybe not a cell. Try to use
let indexPath = collection.indexPathForCell(sender.superView as! UICollectionViewCell)
or
let indexPath = collection.indexPathForCell(sender.superView!.superView as! UICollectionViewCell)
That may work.
I've wrote up a quick example to show you, it uses a tableView but the concept is the same:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var things = [1,2,3,4,5,6,7,8] // These can be anything...
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let objectForCell = self.things[indexPath.row] // Regular stuff
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let objectAtIndex = self.things[indexPath.row]
let indexOfObject = indexPath.row
self.performSegueWithIdentifier("next", sender: indexOfObject)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "next" {
// On this View Controller make an Index property, like var index
let nextVC = segue.destinationViewController as! UIViewController
nextVC.index = sender as! Int
}
}
}
Here you can see you get the actual object itself and use it as the sender in the perform segue method. You can access it in prepareForSegue and assign it directly to a property on the destination view controller.

Resources