How to check if segue completed Swift 3? - ios

I have a segue which passes location data from a table view. I need to add a map annotation after this segue has completed, but the map is my initial VC, so it crashes on load due to the lack of data as the segue has not occurred.
I think I need to wrap the following code (in the viewDidLoad) in an if statement that checks if the segue has occurred, or check if there is any data available to use.
let roomPin = MGLPointAnnotation()
roomPin.coordinate = CLLocationCoordinate2D(latitude: room.latitude, longitude: room.longitude)
mapView.addAnnotation(roomPin)
Not entirely sure how I should be checking if there is any data to use.
edit:
room is a custom class loaded with alamofire
class Room {
var roomNumber : String!
var roomName : String!
var latitude : Double!
var longitude : Double!
init(json : JSON) {
roomNumber = json["room_number"].stringValue
roomName = json["room_name"].stringValue
latitude = json["latitude"].double
longitude = json ["longitude"].double
}
}
then is called in a tableview to display the name and number. When pressed it performs a segue passing the Room object to my mapVC
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mapSegue" {
if let destination = segue.destination as? ViewController {
destination.room = sender as! Room
}
}
}
and in my map view controller I have that above annotation code in the viewDidLoad, with room being declared as:
var room : Room!
Any help is appreciated!

I guess the problem is in the line destination.room = sender as! Room because sender will be of type UITableViewCell class and when you try to typecast sender to Room object type it crashes.

Related

transfer weather data to container view

Im unable to update labels in container view. Here's how i've done it.
​
I wrote my updateWeather function in main VC and retrieved the weather data successfully. when i printed weatherJSON it shows all the received data in console.
now when i started writing updateUI function i could only update the labels on main VC.
so i used prepare segue to send data to container view and sent a string to container VC and updated "humidity" label successfully. all labels accept strings without any issues.
but i have no idea how to send weather data to container view.
i tried passing values using object weatherDataModel but nothing happens. i even declared a new object referring to container view class and used it in updateUI function to set label values but it won't work too.
I have no idea what to pass in place of string to get weather data through to next VC.
override func prepare(for segue: UIStoryboardSegue, sender for: Any?) {
if segue.identifier == "displayFullWeatherInfo"{
let destinationVC = segue.destination as! FullWeatherViewController
destinationVC.delegate = "\(weatherDataModel.pressure)"
....
....
Heres my WeatherDataModel Class:
import Foundation
class WeatherDataModel{
var city = ""
var temp = 0
var country = ""
var humidity = 0
}
in my main VC i have created weatherDataModel object and here's my updateWeatherInfo code:
func updateWeatherInfo(json : JSON){
if let tempDefault = json["data"][0]["temp"].double{
weatherDataModel.temp = Int(tempDefault)
weatherDataModel.city = json["data"][0["city_name"].stringValue
weatherDataModel.country = json["data"][0]["country_code"].stringValue
weatherDataModel.humidity = json["data"][0]["rh"].intValue
updateWeatherUI()
}
else{
currentLocation.text = "Not Available"
}
}
​
Create a property for your weather model object in your second VC FullWeatherViewController:
var weatherDataModel: WeatherDataModel! //your object
And in your first VC:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "displayFullWeatherInfo" {
let destinationVC = segue.destination as! FullWeatherViewController
destinationVC.weatherDataModel = weatherDataModel
}

Passing data between view controllers through segue

I have a MapViewController with a prepareForSegue(_:sender:)method, which I intend to use to send data to LandmarkTableViewController, and is called when a button is pressed.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationvc = segue.destinationViewController
if let landmarkvc = destinationvc as? LandmarkTableViewController {
if let identifier = segue.identifier {
let library = Landmark(name: "Run Run Shaw Library", properties: ["Chinese Kanji", "Gray", "Green Plants"])
let bank = Landmark(name: "Hang Seng Bank", properties: ["Chinese Kanji", "Green"])
switch identifier {
case "showLibrary" : landmarkvc.passedLandmark = library // pass data to LandmarkTableViewController
case "showBank" : landmarkvc.passedLandmark = bank // pass data to LandmarkTableViewController
default : break
}
}
}
}
The LandmarkTableViewController is properly set up to display the String array properties, with one String on each row. So what I intend to do is pass the appropriate data for the table to properties according to which button was pressed, and let LandmarkTableViewController display the corresponding properties.
class LandmarkTableViewController: UITableViewController {
var properties = [String]()
var passedLandmark = Landmark(name: "temp", properties: ["temp"]) // initially set to default value
override func viewDidLoad() {
super.viewDidLoad()
loadSampleProperties()
}
func loadSampleProperties() {
self.properties = passedLandmark!.properties
}
// other methods....
}
class Landmark {
var name: String
var properties: [String]
init?(name: String, properties: [String]) {
self.name = name
self.properties = properties
// Initialization should fail if there is no name or if there is no property.
if name.isEmpty || properties.isEmpty {
return nil
}
}
However, when I run the code, only temp is displayed in the table view. I've been stuck on this for a long time now, so any help is much appreciated!
Edit: loadData() inside of viewDidLoad() is changed to the correct loadSampleProperties(). I made an error while posting the code to the question.
I think this should solve your problem if not double check your identifiers
and you can make sure to data passing with adding print(passedLandmark) to viewDidLoad() or breakpoint to make sure you getting the data
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationvc = segue.destinationViewController
if let landmarkvc = destinationvc as? LandmarkTableViewController {
if segue.identifier == "showLibrary" {
let library = Landmark(name: "Run Run Shaw Library", properties: ["Chinese Kanji", "Gray", "Green Plants"])
landmarkvc.passedLandmark = library
}
if segue.identifier == "showBank" {
let bank = Landmark(name: "Hang Seng Bank", properties: ["Chinese Kanji", "Green"])
landmarkvc.passedLandmark = bank
}
}
}
Hope this will helps
Code is missing from your quote, so I can't be sure, but I assume your loadData() method is the one that reloads the table view data with Landmark you've passed in prepareForSegue. If that is the case:
viewDidLoad() is called before prepareForSegue, so that all the views and elements of the destinationViewController are loaded and ready to use. Thus, in your case, the table view is loaded with your "temp" data and nothing makes it reload when you set the proper one.
You have two options:
You could call loadData()/reloadData() in viewWillAppear for example, which is called after prepareForSegue(). Bare in mind that viewWillAppear will possibly be called again in some other navigation.
Otherwise, you could instantiate and present/push the new controller in your parent view controller, instead of using the segue.

How to pass data to and from a series of view controllers [duplicate]

This question already has answers here:
How do you share data between view controllers and other objects in Swift?
(9 answers)
Closed 6 years ago.
I have been able to pass data from one view to the next.
Now I need to expand a bit and pass this data via three view controllers.
I have three view controllers 1.MileageControler, 2.LocationsControler and 3.LocationChoiceController.
I need to be able to pass data from 1 to 2 then onto 3 and then back to 2.
Mileage Controler has a textbox and a series of numeric buttons that allow the user to enter a mileage. An Enter button passed the following on seque
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "segueLocationView") {
let passingData = segue.destinationViewController as! LocationViewController;
passingData.mileageToPass = DisplayStart.text
passingData.fuelAmountToPass = 0.00
passingData.startLocationToPass = "HOME"
passingData.endLocationToPass = ""
}
}
The LocationController has:
var mileageToPass: String!
var fuelAmountToPass: Double!
var startLocationToPass: String!
var endLocationToPass: String!
override func viewDidLoad() {
super.viewDidLoad()
labelStartMileage.text = "Start Mileage for this trip: " + mileageToPass
labelStartLocation.text = startLocationToPass
labelEndLocation.text = endLocationToPass
}
It also has...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "segueChoiceMade") {
let passingData = segue.destinationViewController as! LocationChoiceViewController;
passingData.mileageToPass = mileageToPass
passingData.fuelAmountToPass = fuelAmountToPass
passingData.startLocationToPass = labelStart.text
passingData.endLocationToPass = txtEndLocation.text
if (sender === but_StartLocation) {
passingData.senderToPass = "Start"
} else if (sender === but_Destination) {
passingData.senderToPass = "End"
}
}
}
So here I'm trying to pass the data on again to the next view (LocationChoice) where the user will choose a location and based on which senderToPass value was passed then the chosen location will be stored and passed back in either startLocationToPass or endLocationToPass. The mileageToPass and fuelAmmountToPass are not changed in LocationChoice just stored and passed back.
The problem I'm having are errors which just show up as... Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) Am I going about this the wrong way....
You could create a new singleton class to hold on to the set of data you are interested in.
Something like this:
import Foundation
class Data: NSObject {
static let sharedData = Data()
var mileage: String?
var fuelAmount: Int?
var startLocation: String?
var endLocation: String?
}
Then in your view controllers you can get and set the values like:
// Set fuel amount in one view controller
Data.sharedData.fuelAmount = 0.00
// And retrieve it in another
let fuelAmount = Data.sharedData.fuelAmount
You're using -prepareForSegue:sender: properly. Your case is pretty simple, so you can get away with setting the values like you're doing, which is great.
The error message provided is a pretty generic "something really bad happened", and there is no backtrace to work from. If you don't have any more info, I recommend setting a breakpoint in the -prepareForSegue:sender: method and stepping line by line until it crashes.
As an aside, I recommend wrapping up the 4 values you're passing around into a model object (with 4 immutable values) and just passing that around.

How to force asynchronously save a constant subclass?

Edit 1: I've restructured my ViewControllers to make it easier to get what I want done.
Edit 2: I realized something major was missing while adding notes to my code, another function overrides the first segue.
This ViewController is where the annotation is created; all I need from this view is for the touchMapCoordinates to be transferred to the other ViewController so I can save the PFGeoPoint in an array.
Edit 3
After long work on understanding what is going on and simplifying the code, i've came down to the final conclusion based off of Swift- variable not initialized before use (but it's not used) , that the current method that I'm trying to use will not work in any case or scenario due to it saving Asynchronously. If anyone knows a work around, then you have officially done something that hasn't been done before :).
Error that is showing up is
Constant 'boi' used before being initialized
Subclass that is declared in Appdata to be used anywhere within the project
import Foundation
import Parse
import MapKit
class MyAnnotation: PFObject, PFSubclassing, MKAnnotation {
// MARK: - Properties
#NSManaged var location: PFGeoPoint
// MARK: - Initializers
init(coordinate: CLLocationCoordinate2D) {
super.init()
self.location = PFGeoPoint(latitude: coordinate.latitude, longitude: coordinate.longitude)
print(location)
}
override class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0;
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
// MARK: - PFSubclassing protocol
static func parseClassName() -> String {
return "AnnotationPins"
}
// MARK: - MKAnnotation protocol
var coordinate: CLLocationCoordinate2D {
return CLLocationCoordinate2DMake(location.latitude, location.longitude)
}
var title: String? = "Start Topic"
}
Where the code will all be saved asynchronously together
} else {
let imageData = UIImagePNGRepresentation(self.galleryCameraImage.image!)
let parseImageFile = PFFile(name: "upload_image.png", data: imageData!)
let boi : MyAnnotation
let textTitleandText = PFObject(className: "AnnotationPins")
textTitleandText["textTopic"] = userTopic.text
textTitleandText["textInformation"] = userText.text
textTitleandText["userUploader"] = PFUser.currentUser()
textTitleandText["imageFile"] = parseImageFile!
textTitleandText["location"] = boi.location
textTitleandText.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
if error == nil {
If anyone could help it would be really appreciated!
Over ride prepareForSegue method like below.
override func prepareForSegue(segue: UIStoryboardSegue, sender:
AnyObject?) {
if segue.identifier == "SegueID" {
let destinationVC = segue.destinationViewController as! DestinationViewController
// Create property in destinationView controller & assign required data from here.
}
}
Hope it helps.
Lets treat your Location data as a normal data to be transferred through segues.
You can use this method to configure your destination View controller variable(same type) that will hold your location data.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//check your segue name
if segue.identifier == "YourSegueIdentifier" {
let destinationVC = segue.destinationViewController as! YourDestinationViewController
destinationVC.locationVariableInDestinationVC = locationVariableInCurrentVC
}
}
Above is the simplest way to pass data via segue, you can use the same approach for your location data too.
Hope that helps!!
Update: Based on your updated code
Move func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {} out of your handleLongPress function. PrepareForSegue function gets called automatically when there is any navigation happening through segues..
If you want to initiate a segue navigation programatically then assign a identifier to the segue and just call self.performSegueWithIdentifier("YourSegueIdentifier", sender: nil)

Passing NSManagedObject from one view controller to another

I have a uitableview which is filled with core data objects. I need to pass object for selected row to detail view controller. Following is my code for it:
Alert Screen:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowAlertDetails" {
if let destination = segue.destinationViewController as? AlertDetailsViewController {
if let blogIndex = tblvwAlerts!.indexPathForSelectedRow()?.row {
let objAlert:Alert = arrReferrals[blogIndex] as! Alert
destination.objAlert = objAlert
}
}
}
}
Detail View Controller:
class AlertDetailsViewController: UIViewController {
#IBOutlet weak var tblvwHitDetail: UITableView?
var objAlert:Alert = Alert()
I am getting following error when I am trying to copy object from first page to detail page:
CoreData: error: Failed to call designated initializer on NSManagedObject class 'Alert'
The error is caused by calling Alert() method in var objAlert:Alert = Alert(). You can set Alert as an Implicitly unwrapped value var objAlert:Alert!. It will be initially nil and then it will hold the reference of the managed objected which is passed from the previous controller.

Resources