Passing data with prepareForSegue doesn't work - swift

I am using the MapKit and when you click on an annotation balloon you get directed to another view. The function I am using for this is:
func mapView(mapView: MKMapView!, annotationView: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
//get tag here
if(annotationView.tag == 0){
//Do for 0 pin
}
if control == annotationView.rightCalloutAccessoryView {
performSegueWithIdentifier("mapToCity", sender: self)
}
}
But the problem is that I can't pass the marker data to the next view. I tried using this function in the first view but I don't exactly know how to retrieve the data in the next view or if it is even saved in "CityDetails":
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "mapToCity") {
var yourNextViewController = (segue.destinationViewController as! CityDetails)
}
}
In the second viewcontroller this:
println(CityDetails);
Only returns the location of CityDetails which is in my case Ontdek_Polen.CityDetails

If you want to use segue to pass data from one view to another view then use this code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "mapToCity") {
var yourNextViewController = segue.destinationViewController as! CityDetails
yourNextViewController.foo = "This is passed"
}
}
In your destination view don't forget to create an instance which will hold this value like this:
import UIKit
class CityDetails: UIViewController {
var foo = "" //this will hold your value
override func viewDidLoad() {
super.viewDidLoad()
println(foo) //This is passed
}
}
Don't forget to set the type of your instance in next view same as you want to pass from previous view.
And one more way to pass data from one view to another view is you can use NSUserDefaults as shown in below example:
func mapView(mapView: MKMapView!, annotationView: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
//get tag here
if(annotationView.tag == 0){
//Do for 0 pin
}
if control == annotationView.rightCalloutAccessoryView {
//set your object here
NSUserDefaults.standardUserDefaults().setObject("abcd", forKey: "yourKey")
performSegueWithIdentifier("mapToCity", sender: self)
}
}
This will store this value into disk so you can read this value anywhere into your project with yourKey.
This way you can read value from NSUserDefault:
import UIKit
class CityDetails: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let foo1 = NSUserDefaults.standardUserDefaults().objectForKey("yourKey") as! String
println(foo1) //abcd
}
}
And for more Info check THIS sample project where I am passing value with segue.
Hope this will help.

There is no reason to use the code (segue.destinationViewController as! CityDetails).
What I would recommend doing is instead of using the last bit of code to access the destination view controllers items just create an instance of the class and access its variables and data through dot notation.
ex. (in this example I am saying that the name of the destination vc is CityDetails)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "mapToCity") {
var yourNextViewController: CityDetails = CityDetails()
yourNextViewController.variable = 0 //example
}
}

Related

Swift Send data from Popup to Another View

I'm trying to send data from popup view to DataView.
it actually works ! .However, when I go back to popup view to edit the text it doesn't show the text that was entered and sent to DataView.
I'm sending the data through protocol.
PopupView
protocol DataEnteredDelegate: class {
func userDidEnterInformation(data: String)}
#IBAction func DoneButton(_ sender: Any) {
if let data = openTextView.text {
delegate?.userDidEnterInformation(data: data)
self.dismiss(animated: true, completion: nil)
}
}
DataView
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openSummary" {
let sendingVC: popupViewController = segue.destination as! popupViewController
sendingVC.delegate = self
}
}
// Protocol receving data from popup
func userDidEnterInformation(data: String) {
addJobSum.text = data
}
After the PopupViewController gets dismissed, it gets destroyed and no new instances are able to know what an old instance had as a text value for the Text View.
To fix it - create a string property inside PopupViewController and initialize the Text View's text property with its value inside some method, such as viewDidLoad():
class PopupViewController: UIViewController {
var textData: String?
/* more code */
func viewDidLoad() {
/* more code */
if let data = textData {
openTextView.text = data
}
}
}
Then, you'll have to inject the proper / needed text for textData inside prepare(for:) method (right before it's presented):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openSummary" {
let sendingVC = segue.destination as! PopupViewController
sendingVC.delegate = self
// This is the new line to be added:
sendingVC.textData = addJobSum.text
}
}

Sort cells inside a tableview alphabetically from a different viewcontroller

I have two scenes. Scene A is a tableview consisting of a list of fruits. Scene B has a segmented controller with one of the options to sort the fruit alphabetically.
scene A:
#IBOutlet weak var tableView: UITableView!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showFilter" {
_ = segue.destinationViewController as! FilterViewController
}
}
?func alpheticalOrder(sender: AnyObject) {
fruits.sortInPlace({$0.name < $1.name }) } ?
scene B:
func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("FilterCell") as! FilterCell
cell.segmentedController.addTarget(self, action: "segmentedControllerActionChanged:", forControlEvents: .ValueChanged)
return cell
}
#IBAction func segmentedControllerActionChanged(sender: AnyObject) {
if sender.selectedSegmentIndex == 0 {
fruits.sortInPlace({$0.name < $1.name })
}
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: "close:")
greyView.addGestureRecognizer(tap)
}
func close(tap: UITapGestureRecognizer) {
self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
}
It's the ViewController().tableView.reloadData() line that is incorrect here, although I put it in to help understand what I'm trying to achieve. Somehow I need to reload the tableview data to sort cells alphabetically by fruit name after exiting scene B to return to scene A.
Consider the line of code:
ViewController().tableView.reloadData()
That does not tell the existing ViewController to reload. The ViewController() creates a new instance of that view controller (which not connected to any storyboard scene; has no data; and will be immediately deallocated), and tells it to reload itself.
You need to have B refer to the existing A, not create a new one. Also, you presumably don't want to sort fruits in B, but rather back in A. Thus:
add a property in B that points back to A:
var sourceViewController: ViewControllerA!
in the prepareForSegue of A, you have to set this property in B, e.g.:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showFilter" {
let filterViewController = segue.destinationViewController as! FilterViewController
filterViewController.sourceViewController = self
}
}
implement method in A that sorts the data and reloads the table:
func sortFruitsAndReload() {
fruits.sortInPlace {$0.name < $1.name }
tableView.reloadData()
}
the segmentedControllerActionChanged in B should call that method in A:
#IBAction func segmentedControllerActionChanged(sender: AnyObject) {
if sender.selectedSegmentIndex == 0 {
sourceViewController.sortFruitsAndReload()
}
}
Or, even better, use a protocol to keep these two classes "weakly coupled" (i.e. opens the door to use this filter/sorting view controller in other situations):
add a protocol in B:
protocol FilterViewDelegate {
func sortAndReload()
}
add a property in B to maintain reference to this delegate:
var delegate: FilterViewDelegate?
specify the A will conform to this protocol:
class ViewControllerA: UIViewController, FilterViewDelegate { ... }
clearly, keep the name of A's view controller class and base class to whatever you are using now, but just add FilterViewDelegate to the class declaration;
implement method in A that sorts the data and reloads the table in order to satisfy the requirements of conforming to this protocol:
func sortAndReload() {
fruits.sortInPlace {$0.name < $1.name }
tableView.reloadData()
}
in the prepareForSegue of A, you set this delegate property in B, e.g.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showFilter" {
let filterViewController = segue.destinationViewController as! FilterViewController
filterViewController.delegate = self
}
}
the segmentedControllerActionChanged in B should call that method in A.
#IBAction func segmentedControllerActionChanged(sender: AnyObject) {
if sender.selectedSegmentIndex == 0 {
delegate?.sortAndReload()
}
}

Delegate using Container View in Swift

I'm developing an app for iPad Pro. In this app, containerView use to add additional views and interact with them.
First, I created a protocol:
protocol DataViewDelegate {
func setTouch(touch: Bool)
}
Then, I created my first view controller
import UIKit
class ViewController: UIViewController, DataViewDelegate {
#IBOutlet var container: UIView!
#IBOutlet var labelText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func setTouch(touch: Bool) {
if touch == true {
labelText.text = "Touch!"
}
}
}
And finally, I created a view that will be embedded in containerView.
import UIKit
class ContainerViewController: UIViewController {
var dataViewDelegate: DataViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func touchMe(sender: AnyObject) {
dataViewDelegate?. setTouch(true)
}
}
But for some reason, nothing happened, the first view controller receives nothing in setTouch function.
My question is: In this case, using container, how can I make the communication between two ViewsControllers?
Like #nwales said you haven't yet set the delegate. You should do set the delegate in prepareForSegue function on your first viewController (who contain the viewContainer)
First select the embed segue and set an identifier in the attributes inspector.
Then in the parentViewController implement the func prepareForSegue like this:
Swift 4+:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "the identifier") {
let embedVC = segue.destination as! ViewController
embedVC.delegate = self
}
}
Below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if (segue.identifier == "the identifier") {
let embedVC = segue.destinationViewController as! ContainerViewController
embedVC.dataViewDelegate = self
}
}
Looks like you defined the delegate, but have not set the delegate. This happens to me all the time.

Swift: I need to get an index of one of UIVIew collections to know which is tapped

I need to get an index of one of UIView collections to know which is tapped then link to a corresponding array element in the next screen. Problem seems sender! that I have the error message:
Could not cast value of type 'UITapGestureRecognizer' (0x107dcdc20) to 'UIView' (0x107dc2578).
(I used UIView to create color tiles. Should I use label or buttons instead of UIView?)
import UIKit
class ColoringViewController: UIViewController {
var coloringItem: ColoringItem?
var colorListTileArray = [UIView]()
#IBOutlet var colorListTile: [UIView]! // UIView color tiles
override func viewDidLoad() {
super.viewDidLoad()
if coloringItem != nil {
for (var i = 0; i<colorListTile.count; i++) {
colorListTileArray += [colorListTile[i]]
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showColoringDescriptionSegue" {
let tappedTile = sender!.self //<- doesn't work
//let tappedTile = sender!.view as! UIView <- doesn't work either :(
let colorIndex = colorListTileArray.indexOf(tappedTile as! UIView)
print(colorIndex)
}
}
#IBAction func showColoringDescription(sender: AnyObject) {
performSegueWithIdentifier("showColoringDescriptionSegue", sender: sender)
}
}
You are close. The sender to showColoringDescription is the UITapGestureRecognizer. Change the signature to take one of those. Then pass sender.view to performSegueWithIdentifier.
In prepareForSegue, cast sender to UIView and use that for the lookup in the colorListTileArray:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showColoringDescriptionSegue" {
let tappedTile = sender as! UIView
let colorIndex = colorListTileArray.indexOf(tappedTile)
print(colorIndex)
}
}
#IBAction func showColoringDescription(sender: UITapGestureRecognizer) {
performSegueWithIdentifier("showColoringDescriptionSegue", sender: sender.view)
}
Since you're using a UITapGestureRecognizer to initiate the segue, the recognizer will be the sender in prepareForSegue You can get the associated view using the view property:
if let recognizer = sender as UITapGestureRecognizer {
let view = recognizer.view
...
}

How pass data from Mapkit to viewcontroller

I want to pass the data (quelmonument) from a pinpoint on a Mapkit to another viewcontroller. With this code just below, i could get the good value but i don't know i could do to pass the data.
func mapView(mapView: MKMapView!, annotationView: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == annotationView.rightCalloutAccessoryView {
if let pinannotation = annotationView.annotation as? Artwork{
println("status was = \(pinannotation.quelmonument)")
var indexmonument = pinannotation.quelmonument
performSegueWithIdentifier("hugo", sender: self)
}
}
}
I have tried to send the value 2, and it's working, but instead of 2 i would like send "quelmonument" variable. Could you give me pls an hint ?
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "hugo"
{
if let destinationVC = segue.destinationViewController as? MonumentViewController{
destinationVC.numberToDisplay = 2
}
}
}