I'm new in swift and IOS, i have some problem to pass the dictionary data to other uiview, anyone can help me to fix it?
LessonsTableViewController:
var mylessons = [
["title":"Posture", "subtitle":"Set up your body", "bgimage":"1", "lesimage":"l1"],
["title":"Breathing", "subtitle":"Breathing deeply", "bgimage":"2", "lesimage":"l2"],
["title":"Breathing", "subtitle":"Breathing Exercise", "bgimage":"3", "lesimage":"l3"],
["title":"Health", "subtitle":"Do’s & Don’ts", "bgimage":"4", "lesimage":"l4"]
]
and
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! LessonsTableViewCell
let lessonsObject = mylessons[indexPath.row]
cell.backgroundImageView.image = UIImage(named: lessonsObject["bgimage"]!)
cell.titleLabel.text = lessonsObject["title"]
cell.subtitleLabal.text = lessonsObject["subtitle"]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "LessonSegue", sender: mylessons[indexPath.row])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
let lessegue = segue.destination as! LessonDetailsViewController
lessegue.SelectedLessons = mylessons
}
LessonDetailsViewController:
#IBOutlet weak var LTitle: UILabel!
var SelectedLessons = [Dictionary<String, String>()]
override func viewDidLoad() {
super.viewDidLoad()
LTitle.text = SelectedLessons["title"]
// Do any additional setup after loading the view.
}
Finally, it has an error "Cannot subscript a value of type '[Dictionary]' with an index of type 'String'.
First your SelectedLessons is wrong type. You need use something like tis
var SelectedLessons:Dictionary<String, String>?
And you need past correct object.
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
let lessegue = segue.destination as! LessonDetailsViewController
lessegue.SelectedLessons = sender as? Dictionary<String,String>
}
You should declare
var SelectedLessons = [String, String]()
Your current declaration is an array of dictionaries
You have a number of problems.
First is a coding style issue. Variable names should start with a lower-case letter, so SelectedLessons should be selectedLessons.
Second, you likely want to pass the user-selected lesson to the destination, not the entire array.
Your array mylessons is an array of dictionaries: (Type [[String:String]])
You should probably name the variable in LessonDetailsViewController selectedLesson (singular, starting with a lower-case letter) and make it type [String: String] (a single lesson.)
Then your prepareForSegue might look like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
guard
let lessegue = segue.destination as? LessonDetailsViewController,
let selectedRow = tableView.indexPathForSelectedRow?.row else {
print("Error. No row selected. Exiting."
fatalError()
}
lessegue.selectedLesson = myLessons[selectedRow]
}
(The code above should have better error handling for the case where there's not a selected row, but it should give you the idea.)
EDIT:
By the way, it's not a good idea to write your prepare(for:) method as if you will only ever segue to a single view controller of a single type. It's very common to go back and expand an app to add additional segues, and if you do that, the code above will crash. Better to use a switch statement:
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
switch segue.destination {
case let lessegue as LessonDetailsViewController:
guard
let selectedRow = tableView.indexPathForSelectedRow?.row else {
print("Error. No row selected. Exiting."
fatalError()
}
lessegue.selectedLesson = myLessons[selectedRow]
default:
print("Unrecognized segue. Exiting."
fatalError()
}
}
That syntax creates a switch statement where each case is executed based on the type of the destination view controller, with a built-in cast to the destination type. It's a neat variant of the switch statement that's very useful in prepare(for:) functions.
Related
I have a UITableView and I am trying to make a segue to another viewcontroller, I need the row number so that I can select a string from an array so that I can display this string in the next view controller, I have this code at the moment.
let tableFrontView = segue.destination as! FCTableFrontViewController
tableFrontView.frontText = path[FlashCardsTableViewCell.init().tag].flashCardFront
the FlashCardsTableViewCell.init().tag is currently returning an int for testing purposes though I am wanting to know what I can replace it with to get me the number of the row which was selected by the user.
Thanks
You can try like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let tableFrontView = segue.destination as! FCTableFrontViewController
let selectedIndexPath = tblView.indexPathForSelectedRow
let selectedRow = (selectedIndexPath?.row)!
print(selectedRow)
}
I assume you are writing the code snippet shown in prepare(for:)?
If that's the case, go to where you perform the segue, which is likely in the didSelectedRowAtIndexPath delegate method. If you don't have such a method, you should implement it.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showTableFrontVC", sender: nil)
}
Replace whatever it is that you are passing as sender now, and replace that with indexPath.row.
performSegue(withIdentifier: "showTableFrontVC", sender: indexPath.row)
Now in prepare(for:), you can unwrap sender as an Int:
let tableFrontView = segue.destination as! FCTableFrontViewController
let rowSelected = sender as! Int
tableFrontView.frontText = path[rowSelected].flashCardFront
Use TableView's delegate method :
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// indetify cell using indexPath.row attribute
}
In Swift 3, I'd like to pass the string contained in a cell of a UITableView into the subsequent View Controller.
As a note, I read several answers to this that seem to be specific to earlier versions of Swift, so I wanted to reopen. As an example, the last solution here looked straightforward, but I can't get it to work.
In TableViewController, I create a string of an audio file's name and date posted with this JSON extraction:
if let shows_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let shows_obj = shows_list[i] as? NSDictionary
{
let episode_name = shows_obj["episode"] as? String
let episode_date = shows_obj["date"] as? String
TableData.append(episode_date! + " | " + episode_name!)
let testString = TableData.append(episode_date! + " | " + episode_name!)
// the part above runs and will add the string into each cell. now here, I want to pass the value of that string into a UILabel called episodeTitle in the target view controller
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let indexPath = self.tableView.indexPathForSelectedRow {
let destinationVC = segue.cellPasser
EpisodeViewController.text = testString //this is the string in second view controller
}
}
}
}
}
This is throwing two errors:
Value of type 'UIStoryboardSegue' has no member 'cellPasser'
In the Storyboard, I have added an identifier to the Show segue called 'cellPasser.' My belief is I should be able to call that here but it's being flagged.
EpisodeViewController.episodeTitle = testString
I have an outlet in EpisodeViewController that is called episodeTitle, so I am wondering if there is a preferred way other than this to pass strings into an element like that.
I'm new to Swift -- hoping someone sees a better way to do this.
Edit: In case useful, here's the View Controller linked from the segue.
class EpisodeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBOutlet var episodeTitle: UILabel!
Here are steps to pass value from TableViewController to next ViewController :
in TableViewController You should declare a didSelectRowAt method.
Declare a method prepare for segue
Do not forget to declare a variable in second view Controller.
1.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "Identifier", sender: indexPath)
}
2.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Identifier" {
let vc = segue.destination as! NameOfYourViewController
vc.variableInSecondVc = variableInTableView
}
}
3.
var variableInSecondVc = ""
I'm making ios app. Please look my table view image below.
If you click add(+) button, you can add ENG word and its meaning in Korean(KOR) in each textfield.
After filling the textfield and click save button (it is located on right-top, "저장"), the word is added like the image below.
word is added
For example, the ENG is endless and the meaning(KOR) is "끝없는".
And, I want to use UIReferenceLibraryViewController .
If i click the cell of the list, i want to show its dictionary.
#IBAction func viewDictionary(_ sender: UITapGestureRecognizer) {
let engDictionaryWord = **engListWord**
let ViewController = UIReferenceLibraryViewController(term: engDictionaryWord)
self.present(ViewController, animated: true, completion: nil)
}
I want to use this method.
But, I don't know how to save my ENG input in engListWord.
In pic2 's swift file(addWordViewController.swift), there is prepare() method like this.
// This method lets you configure a view controller before it's presented.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
// Configure the destination view controller only when the save button is pressed.
guard let button = sender as? UIBarButtonItem, button === saveButton else {
os_log("The save button was not pressed, cancelling", log: OSLog.default, type: .debug)
return
}
let eng = engTextField.text ?? ""
let kor = korTextField.text ?? ""
// Set the meal to be passed to WordTableViewController after the unwind segue.
word = Word(eng:eng, kor:kor)
}
and viewDidLoad() method in addWordViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
engTextField.delegate = self
korTextField.delegate = self
// Set up views if editing an existing Word.
if let word = word{
engTextField.text = word.eng
korTextField.text = word.kor
}
// Do any additional setup after loading the view.
}
I don't know which variable i have to use.
There is other swift file in my project, If i misuploaded that codes above, please tell me! I will edit my question immediately.
Main.Storyboard
If i use GestureRecognizer, i made this code but I don't know it is right...
#IBAction func MyDictionary(_ sender: UITapGestureRecognizer) {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "viewDictionary", sender: indexPath)
}
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// I just made this identifier up, but create one yourself in the storyboard
if segue.identifier == "viewDictionary" {
// Define your vc
let libController = segue.destination as! UIReferenceLibraryViewController
// Define indexPath
let indexPath = sender as! IndexPath
// Set value in destination vc
libController.engWord = words[indexPath.row].eng
}
}
}
I think the way to go is to use the UITableViewDelegate method didSelectRowAtindexPath in the WordTableViewController (if that's the class with your table view).
A gestureRecognizer does not seem like something you want in your tableViewController as it has nice build in delegate methods to register user presses. Therefore, replace your viewDictionary(_:) function with the following:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "viewDictionary", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// I just made this identifier up, but create one yourself in the storyboard
if segue.identifier == "viewDictionary" {
// Define your vc
let libController = segue.destination as! UIReferenceLibraryViewController
// Define indexPath
let indexPath = sender as! IndexPath
// Set value in destination vc
libController.engWord = yourArrayOfEngWords[indexPath.row]
}
}
This will get your eng word of the cell you have pressed and save it to an attribute "engWord" that you then define in your UIReferenceLibraryViewController:
class UIReferenceLibraryViewController(){
var engWord: String = ""
// Rest your class properties and functions here...
}
Once it is set you can use it as you like after the segue has been performed :=)
I am new to Swift and I have this interesting problem.
I am trying to send the label of a table cell when I segue to another view controller where I print it. The problem is that it is printing the label of the cell that was pressed previous to this press.
Here is the code in the main view controller that passes the label:
// When a user taps on a cell on the tableView, it asks for a tag name for that image.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Cell \(indexPath) selected")
// Get cell image.
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRow(at: indexPath!) as! ImageFeedItemTableViewCell
imagePass = currentCell.itemImageView
labelPass = currentCell.itemTitle
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Send image to GreetingViewController.
if segue.identifier == "GoToGreeting" {
var greetingvc = segue.destination as! GreetingViewController
greetingvc.passedImage = imagePass
greetingvc.passedLabel = labelPass
}
}
and here is the relevant code in the view controller that receives the passed label:
var passedImage: UIImageView? = nil
var passedLabel: UILabel? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print(passedLabel?.text)
}
Any help would be appreciated.
I believe prepare(for:sender:) gets called before tableView(_:didSelectRowAt:) when you hook up that segue in the storyboard.
What you can do is just use the sender parameter of the prepare(for:sender:) method to get the information you need at the right time. When a segue is triggered by a cell, as it seems to be in your case, then that cell will be the sender passed into the the prepare method. So, you could do something like:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Send image to GreetingViewController.
if let cell = sender as? ImageFeedItemTableViewCell, segue.identifier == "GoToGreeting" {
var greetingvc = segue.destination as! GreetingViewController
greetingvc.passedImage = cell.itemImageView
greetingvc.passedLabel = cell.itemTitle
}
}
I have this issue with my code, I think the problems appeared after my I changed syntax to new swift version.
import UIKit
class FirstTableViewController: UITableViewController {
var FirstTableArray = [String]()
var passThisArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// This array will display on your tableviewcell.
FirstTableArray = [lib1]
//You can pass element of this array
passThisArray = ["1. Fi "]
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return FirstTableArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let Cell = self.tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
Cell.textLabel?.text = passThisArray[(indexPath as NSIndexPath).row]
return Cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "detailView") {
let vc = segue.destination as! ViewController
//Get the Index of selected Cell
let indexPath : IndexPath = self.tableView.indexPathForSelectedRow!
//assign string to next view controller instance from selected cell.
vc.FirstString = FirstTableArray[(indexPath as NSIndexPath).row]
}
}
}
At this line it gives me 2 errors:
let indexPath : IndexPath = self.tableView.indexPathForSelectedRow!
Value of optional type 'NSIndexPath?' not unwrapped; did you mean to
use '!' or '?'?
Invalid use of '()' to call a value of non-function type
'NSIndexPath?'
Thankyou.
The issue is that there is no guarantee that the user has selected a row, so your call to indexPathForSelectedRow is an optional (it may or may not have a value).
A way to improve this method is to use the guard statement to safely unwrap the two optional values you have in this method. In the event that one of them is not set (nil), the method will safely exit without crashing your app.
One benefit of using guard over the if let ... approach is that you can avoid a pyramid of doom. Using your example, it would require three indents to run the final command where you assign the string, thus making your code harder to read. The guard statement is explicitly saying "if this value fails, guard against crashing".
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "detailView") {
guard let vc = segue.destination as? ViewController else {
return
}
//Get the Index of selected Cell
guard let indexPath = self.tableView.indexPathForSelectedRow else {
return
}
//assign string to next view controller instance from selected cell.
vc.FirstString = FirstTableArray[(indexPath as NSIndexPath).row]
}
}
Also, two minor code style musings:
You do not need to use : NSIndexPath when you assign to indexPath. The compiler can infer the type for you
When declaring variables, iOS convention is to use camelCase, so while your indexPath variable is good, you should change the FirstString to firstString