I am new of the swift3. So, please forgive me if you think it is easy question. Do not have a clear idea when searching internet or stack overflow with my situation, so I ask this question.
Goal: Passing data from a tableview to webview
Example: data 1,2,3... in the table, press 1, then jump into webview with value 1
Information:
In main.storyboard, looks like:
class oneViewController for a view controller with tableview
class twoViewController for a view controller with webview
In oneViewController, things are well set and select row at:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Array[indexPath.row] //This value expected to pass
//some code to execute passing data...
//Also, jump into another view controller with webview
}
In twoViewController, everything got ready:
//let passData = some code to get the data....
let URL = NSURL(string: "https://www.example.com?data=\(passData)")
webView.loadRequest(NSURLRequest(url: URL! as URL) as URLRequest)
Finally, PHP can get the value
echo $_GET["data"];
Questions:
How to set the relationship between tableview and webview in main.storyblard?
Connect view controller to anther view controller? Or connect Table view Cell to view controller? Or something else.....
How to implement passing data in class oneViewController and twoViewController?
Follow the below steps:-
Step 1: Below code will launch and pass the data in your TwoViewController. Please note that in your Main.Storyboard->TwoViewContoller, give the identifier as TwoViewController
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyBoard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let twoVC:TwoViewController = storyBoard.instantiateViewController(withIdentifier: "TwoViewController") as! TwoViewController
twoVC.passData = Array[indexPath.row]
self.present(twoVC, animated: true, completion: nil)
}
Step 2: In your TwoViewController class define a variable as below
var passData: String = ""
you can do three solutions :
1- send data in segue
2- forget the segue relation and call the new controller in didSelectRowAt, and set data like this :
let vc = UIStoryboard(name: "BarcodeScanner", bundle: nil).instantiateInitialViewController()! as! BarcodeScannerViewController
vc.passData = valueWantToSend
self.presentViewController(vc, animated: true, completion: nil)
3- use struct like this and you be able to use your data from any place in your project :
struct Variables
{
static var value = false
static var passData = ""
}
for exemple : Variables.passData=#"new value"
Related
i'm having a problem where my first view controller is just repeating itself and not showing the second view controller, I've watched videos on how to pass data from one view controller to another and i have it all set up the way its supposed to be. it transfers the data to the second view controller properly and I've tested it with Printing the information I'm passing, but any other ui elements won't show up on the second view controller, i think they are being covered by the table view but it doesn't make sense to me and I'm not sure how to test this.
when i press on a table view cell its supposed to open the second view controller
this is the code that sends and presents the second view controller:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
//open another view contoller and show the recipe
let secondvc = self.display![indexPath.row]
let secondvcresources = secondvc.resource
let secondvcdirections = secondvc.directions
let secondvcname = secondvc.name
let vc = CustomSecondViewController(resources: secondvcresources!, directions: secondvcdirections!, name: secondvcname!)
present(vc,animated: true)
}
this is the second view controller:
import UIKit
class CustomSecondViewController: ViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGray
title = name.uppercased()
let textview = UITextView()
textview.frame = view.bounds
}
private let name: String
private let directions: String
private let resources: String
init(resources: String, directions: String, name: String ){
self.resources = resources
self.directions = directions
self.name = name
super.init(nibName: nil, bundle: nil)
print(resources)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Your current code doesn’t work because your CustomSecondViewController‘s init method,
init(resources:directions:name:)
…doesn’t do anything to load the view controller’s views. A CustomSecondViewController you create with that init won’t have any views, and won’t display to the screen.
If you want to load your CustomSecondViewController’s views from a storyboard, you need to use the function instantiateViewController(withIdentifier:) to create it.
Your rewritten tableView(_:didSelectRowAt:) function might look like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
//open another view contoller and show the recipe
let secondvc = self.display![indexPath.row]
let secondvcresources = secondvc.resource
let secondvcdirections = secondvc.directions
let secondvcname = secondvc.name
guard let vc = self.storyboard?.instantiateViewController(withIdentifier: “CustomSecondViewController”) else {
fatalError(“CAn’t load view controller from storyboard”)
}
// you’ll need to refactor your CustomSecondViewController so it’s properties are public vars, not private “lets”
vc.directions = secondvc.directions
vc.resources = seconded.resources
vc.name = secondvc.name
present(vc,animated: true)
}
I've created a view controller that allows the user to search for addresses, using MKLocalSearchCompleter, and the addresses pop up in a tableView. When the user clicks on an address in the tableView, I want to set a button's text in the previous view controller as the address clicked. I did this by setting the title from MKLocalSearchCompletion to a variable I created in the previous viewController. Then I've created a function in the previous viewController to set the variable as the button's title. Then I've called the function in the viewController which searched the button's address when within the didSelectRowAt function. Like this:
in AddViewController (the previous view controller):
#IBOutlet weak var AddressButton: UIButton!
struct addressButtonTitleStruct {
static var addressButtonTitle: String = ""
}
func setAddressButton () {
AddressButton.setTitle(addressButtonTitleStruct.addressButtonTitle, for: .normal)
}
and in AddressSearchViewController (the one used to search for addresses that was presented):
var searchResults = [MKLocalSearchCompletion]()
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let searchResult = searchResults[indexPath.row]
tableView.deselectRow(at: indexPath, animated: true)
AddViewController.addressButtonTitleStruct.addressButtonTitle = searchResult.title
AddViewController().setAddressButton()
dismiss(animated: true, completion: nil)
}
However, no matter what I do- unplugging and then replugging the outlets, trying another outlet or trying the code on another view controller and another class- the app always crashes while trying to select a row: I get "Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value" on AddressButton. I've tested the variable and it shows up correctly all the ways to AddViewController, within the function setAddressButton. The only thing that doesn't seem to be working is the AddressButton itself, which shows up as nil in the variables in the debug area.
Does anyone know what I'm doing wrong?
Great question! At the top of my head, I have two ways you can do that. I'll explain them to you and you decide what's best to your application, alright?
But before that, explaining your mistake, in this piece of code AddViewController().setAddressButton() you're actually creating a new instance of your view controller and calling the function. You're not using the preexisting view controller.
Unfortunately, billhack's answer has the same wrong approach, essentially creating a new instance of the view controller every time.
Ok, let's do it:
First, and actually tested by me:
Create a callback function on the second view controller like that:
var callBack:((_ buttonTitle: String) -> ())?
Make sure you set that variable with some value before popping the second view controller, like this:
callBack?("Some Title")
In the push function of the first view controller, write something like this:
#IBAction func pushToSecondVC(_ sender: Any) {
if let secondVC = storyboard?.instantiateViewController(withIdentifier: "SecondViewController")as? SecondViewController {
vc.callBack = { buttonName in
self.setButtonName(buttonName)
}
self.navigationController?.pushViewController(secondVC, animated: true)
}
Second (as a guess of mine, I've not actually tried it), get the previous View controller and call the function:
let prev = self.presentingViewController
prev.callTheFunction()
To follow up vadian's comemnt, here is how to grab the right vc instance
let storyboard = UIStoryboard(name: "mystoryboard", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "VCIdentifier") as! VCType
I got it to work by using a callback function like Savio suggested. I ended up calling my callback function a bit differently from what he did:
On the second view controller, the one that popped up and let me search addresses, I did this:
var callBack: ((_ addressButtonText: String) -> ())?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let searchResult = searchResults[indexPath.row]
tableView.deselectRow(at: indexPath, animated: true)
callBack?(searchResult.title)
dismiss(animated: true, completion: nil)
}
and on the first view controller, the one that presented the Address Search View Controller, I did this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "AddViewControllerToAddressSearchViewController" {
let popup = segue.destination as! AddressSearchViewController
popup.callBack = setAddressButton(_:)
}
}
func setAddressButton (_ addressButtonText: String) -> () {
addressButton.setTitle(addressButtonText, for: .normal)
}
This video really helped me: https://www.youtube.com/watch?v=p80Kqj6rV50
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.
I have a tableview that has data stored in core data and when the tableviewCell is tapped it opens another viewController with a label with the data on it. I want to have a textfield and a button on that viewController that when the button is tapped it updates the tableviewCell data and saves in core data.
Here is some code when I pass the data over
Main View Controller:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
var name2 = String()
tableView.deselectRow(at: indexPath, animated: true)
let name = itemName[indexPath.row]
name2 = (name.value(forKey: "title") as? String)!
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondVC = storyboard.instantiateViewController(withIdentifier: "secondView") as! SecondViewController
secondVC.name = name2
self.present(secondVC, animated: true, completion: nil)
}
Here is in the other view controller
var name = ""
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = name
}
Now all I need is to be able to edit one of those tableviewCells from the second view controller and send it into the tableview in the first and update core data.
I would recommend you to save your data in second view controller to core data and fetch the same in first view controller and display in the table.
i got an app without storyboards, all UI creation is made in code and I got a splitView which I would make it usable on iPhone, because as the app as been first designed for iPad only, so that when you select a row in the list in the Master view it does nothing on iPhone but is working fine on iPad.
So my question is can I create and perform the segue that allows to show the Detail View on the didSelectRowAtIndexPath method ?
Here's what i've done so far :
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let segue = UIStoryboardSegue(identifier: "test", source: self, destination: detailViewController!)
performSegueWithIdentifier("test", sender: self)
}
but when running and selecting a row the app was crashing telling it needed a performhandler so i added this :
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let segue = UIStoryboardSegue(identifier: "test", source: self, destination: detailViewController!, performHandler: { () -> Void in
let object = self.fetchedResultsController.objectAtIndexPath(indexPath)
let controller = self.detailViewController!
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
})
performSegueWithIdentifier("test", sender: self)
}
and now when selecting a row xcode says that there is no segue with such identifier "test".
I also tried to call it by segue.perform() and add the performHandler content into the prepareForSegueMethod :
if segue.identifier == "test" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath)
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
and it does just nothing, doesn't crash, just highlight the row i selected and that's all
Can you guys help me ?
EDIT : As Oleg Gordiichuk said, it's not possible to do what I want to do without Storyboards, so thanks for his help :)
Segue it is component of the storyboard interaction it is possible to understand from name of the class UIStoryboardSegue. It is bad idea to create segues programmatically. If i am not making mistake storyboard creates them for you.
For solving of you're issue try to use some common ways like simply present ViewController.
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("id") as! MyController
self.presentViewController(vc, animated: true, completion: nil)
As i understand from our conversation in comments. You would like to create navigation for tableview to details view using segues without storyboard. For now it is impossible to do this without storyboard.
For future learning try to investigate this information.
One way is to use the didSelectRow method for tableView
Swift 3.x
// MARK: - Navigation & Pass Data
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected Row \(indexPath.row)")
let nextVC = YourNextViewController()
nextVC.YourLabel.text = "Passed Text"
nextVC.YourLabel.text = YourArray[indexPath.row]
// Push to next view
navigationController?.pushViewController(nextVC, animated: true)
}