Passing VC data not working properly - ios

When a user taps on an image in my application, I would like for that to send them to another view controller to give them more post details but I can't quite figure out how to transfer that image from one view controller to the next!
The code below, should they tap the image, should send them to the detailed view controller - this is done so in the else if statement.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if username != PFUser.currentUser()?.username {
let profileVC: UserProfileViewController = segue.destinationViewController as! UserProfileViewController
profileVC.usernameString = username
} else if (segue.identifier == "toPostDetail") {
var svc = segue.destinationViewController as! PostDetailsViewController
svc.toPass = // Call/pass the image
}
}
To retrieve all the posts in my database I use the following code, this runs perfectly fine but I need to get the postImage trasnfered to the next view controller.
postsArray[indexPath.row]["image"].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
postCellObj.postImage.image = downloadedImage
postCellObj.postImage.layer.masksToBounds = true
postCellObj.postImage.layer.cornerRadius = 5.0
}
}
So what would I put in the svc.toPass in order for the image to be sent to the other view controller? Since this is an array of posts, would I have to do something with didSelectRowAtIndexPath or am I doing the right thing? I am also using Parse.com to get information to and from my databases.

You should pass the post rather than the image, so it would be something like
postsArray[indexPath.row]
How you get the indexPath is up to you. If the segue is triggered by a cell selection then you could store the indexPath in didSelectRowAtIndexPath, or you could pass the indexPath as the sender when you trigger the segue, or you could use indexPathForSelectedRow if it's retained during the segue process.

Related

How to access custom annotation properties when using calloutAccessoryView as the sender of a segue to a new viewcontroller?

I have the following code to prepare for my segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Make sure we are acting on the correct segue
if segue.identifier == "CreateJumpSpot", let jumpSpotCreatorControllerVC = segue.destination as? JumpSpotCreatorController {
// Set the delegate in the JumpSpotCreatorController we're navigating to
jumpSpotCreatorControllerVC.delegate = self
} else if segue.identifier == "JumpSpotInfo", let jumpSpotInfoVC = segue.destination as? JumpSpotInfoController {
if let senderAnnotationView = sender as? JumpSpotAnnotationView {
jumpSpotInfoVC.titleLabel.text = senderAnnotationView.annotation?.title as? String
jumpSpotInfoVC.imageView.image = senderAnnotationView.annotation.
}
}
}
We are focusing on the 'else if' part of the statement here. I have a custom annotation and annotation view. I am populating labels and imageViews in the view controller that I am segueing to, using the properties of the annotation that the user clicked on to reveal the .detailDisclosure version of the rightCalloutAccessoryView. However that sender (.detailDisclosure of rightCalloutAccessoryView) is only allowing me to access the title and subtitle of the annotation. As you can see when I got to the image property I stopped typing, as there was no property to access. How can I access the properties of my custom annotation?
Can‘t you just get the image by senderAnnotationView.annotation?.image, just like what you are doing to get the title?
PS: Don't depend too much on Xcode autocompletion. Sometimes it's doesn't work perfectly well.
Ok I figured it out. All I had to do was adjust the code so that I had a constant of the annotation itself, and cast it as my custom class. Here's the code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Make sure we are acting on the correct segue
if segue.identifier == "CreateJumpSpot", let jumpSpotCreatorControllerVC = segue.destination as? JumpSpotCreatorController {
// Set the delegate in the JumpSpotCreatorController we're navigating to
jumpSpotCreatorControllerVC.delegate = self
} else if segue.identifier == "JumpSpotInfo", let jumpSpotInfoVC = segue.destination as? JumpSpotInfoController {
if let senderAnnotationView = sender as? JumpSpotAnnotationView {
let senderAnnotation = senderAnnotationView.annotation as? JumpSpotAnnotation
jumpSpotInfoVC.titleLabel.text = senderAnnotation?.title
jumpSpotInfoVC.imageView.image = senderAnnotation?.image
jumpSpotInfoVC.descriptionLabel.text = senderAnnotation?.description
jumpSpotInfoVC.heightLabel.text = senderAnnotation?.estimatedHeight
jumpSpotInfoVC.warningsLabel.text = senderAnnotation?.warnings
}
}
}
The key line there is: let senderAnnotation = senderAnnotationView.annotation as? JumpSpotAnnotation

Transfer data using prepare for segue

I'm new to swift and firebase services,
I'm using fire store data base as my database and I have a first table view that reads all the data and put it in a nice tableview. every document in my table view has a sub collection. when a user press a row I want it to open a second table view with the sub collection.
this is my prepare for segue code :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = tableViewDishes.indexPathForSelectedRow {
db.collection("Restaurants").document("Applebees").collection("Menu").document(sections[indexPath.section].sectionName!).collection("Dishes").document(sections[indexPath.section].listofDishes![indexPath.row].DishName).collection("Options").getDocuments { (querySnapshot, error) in
if error != nil {print(error)}
else {
for document in querySnapshot!.documents {
//adding all the data to an array called myOption
}
}
}
let selectedDishTableViewController = segue.destination as! SelectedDishViewController
selectedDishTableViewController.myOption = self.myOption
selectedDishTableViewController.dish = self.sections[indexPath.section].listofDishes?[indexPath.row]
selectedDishTableViewController.sectionName = sections[indexPath.section].sectionName!
self.myOption.removeAll()
}
}
the issue is that once my code reach the db.collection line it jumps right away to after the for loop when myOption is a empty array and only then it comes back and appending objects to my array.
that cause the first time I press a row get an empty second table view and when I go back and press it again I get the required information.
db.collection() does some async work, so all this code:
let selectedDishTableViewController = segue.destination as! SelectedDishViewController
selectedDishTableViewController.myOption = self.myOption
selectedDishTableViewController.dish = self.sections[indexPath.section].listofDishes?[indexPath.row]
selectedDishTableViewController.sectionName = sections[indexPath.section].sectionName!
self.myOption.removeAll()
should be right before the for loop where you set myOption array. That way, everything will be set up once the database has retrieved all the data and you have made all the setup inside the for loop.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = tableViewDishes.indexPathForSelectedRow {
db.collection("Restaurants").document("Applebees").collection("Menu").document(sections[indexPath.section].sectionName!).collection("Dishes").document(sections[indexPath.section].listofDishes![indexPath.row].DishName).collection("Options").getDocuments { (querySnapshot, error) in
if error != nil {print(error)}
else {
for document in querySnapshot!.documents {
//adding all the data to an array called myOption
}
let selectedDishTableViewController = segue.destination as! SelectedDishViewController
selectedDishTableViewController.myOption = self.myOption
selectedDishTableViewController.dish = self.sections[indexPath.section].listofDishes?[indexPath.row]
selectedDishTableViewController.sectionName = sections[indexPath.section].sectionName!
self.myOption.removeAll()
}
}
}
}
Since its an Async call you might have to write the selectedDishTableViewController segue in the callback.
Recommendation : Seque delegate method should be simple and should not be handling all these db operation try to optimise it.

String not being passed to next view controller from prepareForSegue

I have a push segue on my StoryBoard which is named toGuestVC.
I use that to segue to the next ViewController in my didSelectRowAtIndexPath method like so:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let username = followUsernameArray[indexPath.row]
performSegue(withIdentifier: SG_TO_GUEST_VIEW_CONTROLLER, sender: username)
}
Then in my prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == SG_TO_GUEST_VIEW_CONTROLLER {
if let nextVC = segue.destination as? GuestCollectionVC, let sender = sender as? String {
print("PRINTING NEXT VC: \(nextVC)") //This prints out the memory address. Not sure if this is what you meant by print nextVC.
nextVC.guestUser = sender
}
}
}
For some reason this line in my prepareForSegue is not running:
nextVC.guestUser = sender.username
When I try to print out the value guestUser in my nextViewController the value of guestUser is nil. But when I print out the value of sender in my prepareForSegue method it is not nil.
So is my sender value not being passed to the next ViewController? I can't find a solution to this problem any ideas?
GuestCollectionVC Implementation:
import UIKit
import Parse
private let reuseIdentifier = "Cell"
class GuestCollectionVC: UICollectionViewController {
var guestUser: String!
override func viewDidLoad() {
super.viewDidLoad()
print("PRINTING SELF IN GuestCollectionVC: \(self)")
loadPosts()
}
func loadPosts() {
//Load posts query
let query = PFQuery(className: PF_POSTS_CLASS)
query.limit = postCount
//Getting error here below this comment when I use guestUser since it is nil
query.whereKey(PF_POSTS_USERNAME_COLUMN, equalTo: guestUser)
query.findObjectsInBackground { (result: [PFObject]?, error: Error?) -> Void in
if error == nil {
if let result = result {
self.uuidOfPosts.removeAll(keepingCapacity: false)
self.imageArrayOfPFFIle.removeAll(keepingCapacity: false)
for postObject in result {
if let uuid = postObject[PF_POSTS_UUID_COLUMN] as? String, let pic = postObject[PF_POSTS_PIC_COLUMN] as? PFFile {
self.uuidOfPosts.append(uuid)
self.imageArrayOfPFFIle.append(pic)
}
}
self.collectionView?.reloadData()
}
}else if error != nil {
print("ERROR FROM GUEST COLLECTION VC FROM loadPosts FUNCTION: \(error?.localizedDescription)")
}
}
}
}
So this is my implementation in the GuestViewController. In my loadPosts method where I used the variable guestUser I am getting the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
From console printing
PRINTING NEXT VC: "InstagramClone.GuestCollectionVC: 0x7a6c5cc0"
PRINTING SELF IN GuestCollectionVC: "InstagramClone.GuestCollectionVC: 0x7a6c5100"
it's now obvious that hidden unexpected instance of GuestCollectionVC was created. So, there are different errors occurs depending on order of this two objects invoke their viewDidLoad method (can be any order). Also there are can be other errors in nextVC viewDidLoad method, but this is other story for other question.
You got this problems because you created action segue, that works automatically on cell click (hidden view controller created), and at the same time you are perform this segue in code, creating second controller nextVC.
To solve issue, you should find and remove that segue and add new one, not action segue from some element of your view controller, but "empty" segue between view controllers. To create segue of this type you should select first view controller, hold "control" key and start dragging to next view controller from yellow square on top of first view controller (that symbol controller itself), choose "show", and set identifier.
Since I don't have the full context of the values within your tableView method I can only speculate. That said, the sender you're passing in should be the view controller:
performSegue(withIdentifier: "toGuestVC", sender: username)
You're passing in a value called username which looks to be a string value? It should be something like:
performSegue(withIdentifier: "toGuestVC", sender: self)
where self is your view controller. If you're passing in a string value to sender then in your prepareForSegue method then sender does not have a property called username sender actually is username. Therefore you should pass the value elsewhere:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = self.tableView.indexPathForSelectedRow
if let nextVC = segue.destination as? GuestCollectionVC {
nextVC.guestUser = followUsernameArray[indexPath.row]
}
}

move data to another view controller, but the 2nd view controller load first before 1st view controller

i want to make when user login in the login view controller, the email saved and display to slide out menu view controller, but the slide out menu displayed first when build and run. So the variable contains nothing because user haven't login yet in login view controller.
im using this to declare the text in text field is email fill
let emailFill:String = self.emailTextfield.text!
and use
self.labelContainEmail = emailFill
the labelContainEmail is a global variable at top
var labelContainEmail: String = ""
i'm using prepare for segue like this to move data
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if segue.identifier == "yolo"
{
let vc = segue.destinationViewController as! SlideOutMenu
vc.labelContainEmail2 = labelContainEmail
print(vc.labelContainEmail2) // this don't print anything, but when i print under `self.labelContainEmail = emailFill` it printed out the email
}
}
in the slideOutMenu.swift, i have this var at top
var labelContainEmail2: String = ""
and try to change the label text with
labelEmail.text = labelContainEmail2
in the view did Load
what should i do? please go easy, i'm new to swift
using prepare for segue store email in NSUserDefaults
NSUserDefaults.standardUserDefaults().setObject(labelContainEmail , forKey: "email") // save user email
in the slideOutMenu.swift and try to change the label text with in the view did Load
if (NSUserDefaults.standardUserDefaults().objectForKey("email")) == nil
{
NSUserDefaults.standardUserDefaults().setObject("" , forKey: "email")
}
else
{
let emailStr = NSUserDefaults.standardUserDefaults().objectForKey("email") as! String
}
You can use on the top this line
** Second View
class SlideOutMenu: UIViewController {
var labelContainEmail: String?
//your code here
}
Where you are going from first view to second view.
**Use First View
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if segue.identifier == "yolo"
{
let vc = segue.destinationViewController as! SlideOutMenu
vc.labelContainEmail2 = labelContainEmail
print(vc.labelContainEmail2) // this don't print anything, but when i print under `self.labelContainEmail = emailFill` it printed out the email
}
}
i hope it will help you other wise you can use Model Class also better way for pass data.

How to update managed object data?

I have started my first core data application. I am working with one entity right now called 'Folder'.
The first view controller displays all the Folders in a tableview, which I can add to and it reloads the data. This works fine because It uses the fetch request to populate the table.
override func viewWillAppear(animated: Bool) {
var error: NSError?
let request = NSFetchRequest(entityName: "Folder")
request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
self.events = moc?.executeFetchRequest(request, error: &error) as! [Folder]
self.UITable.reloadData()
}
However when segueing to another view controller via the table cell I pass on the selected Folder data to the controller using the index path. e.g.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "showDetails" {
let destinationVC = segue.destinationViewController as! FolderDetailsViewController
let indexPath = UITable.indexPathForSelectedRow()
let selectedFolder = folders[indexPath!.row]
destinationVC.selectedFolder = selectedFolder
}
}
My second view controller uses the data passed from the first table view to display in textfields:
var selectedFolder: Folder!
folderNameLabel.text = selectedFolder?.title
folderDetailsLabel.text = selectedFolder?.details
folderDateLabel.text = displayDate
I then have a modal to edit/save the folder data in a modal appearing from the second controller:
//Edit and save event
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
//Error
var error: NSError?
//Storing Data from fields
SelectedFolder!.title = FolderName.text
SelectedFolder!.details = FolderDetails.text
SelectedFolder!.date = FolderDate.date
context?.save(&error)
self.dismissViewControllerAnimated(true, completion: {});
When dismissing the modulate data is not updated, I have to go back to the first controller to reload the data and segue again.
I think this is because I have no NSFetchRequest (or NSFetchResultsController) to get the most recent changes.
What is the best method to reload the data of the selectedFolder when I make the changes in the modal ?
You can refresh your second view in viewWillAppera() if your modal view is presented in full screen.
override func viewWillAppear(animated: Bool) {
{
folderNameLabel.text = selectedFolder?.title
folderDetailsLabel.text = selectedFolder?.details
folderDateLabel.text = displayDate
}
It seems like you would want to call moc.refreshObject(folder, mergeChanges:true)
See the documentation here.

Resources