Value is not passed after prepareforsegue. Swift - ios

VC that fires perform segue.
It has a backgroundImage with a userImage and a collectionView with images in cells.
import UIKit
class EmojiCollectionVC: {
#IBOutlet weak var backgroundImage: UIImageView!
#IBOutlet weak var emojiCollection: UICollectionView!
var userImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
backgroundImage.image = userImage
}
#IBAction func dismiss(_ sender: Any) {
dismiss(animated: false, completion: nil)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! EmojiCollectionCell
let chosenEmoji = cell.emojiView.image
performSegue(withIdentifier: "backToEmojiVC", sender: chosenEmoji)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "backToEmojiVC"{
if let destinationVC = segue.destination as? EmojiVC {
if let emoji = sender as? UIImage {
destinationVC.emojiImage = emoji
let data = UIImagePNGRepresentation(userImage)
print("Zhenya: 1 - \(data)")
destinationVC.imageData = data
print("Zhenya: 5 - \(destinationVC.imageData)")
}
}
}
}
}
userImage has an image in it that is displayed.
After pressing the cell, image from it (chosenEmoji) should be passed to EmojiVC, to its emojiImage.
Both prints "Zhenya: 1" and "Zhenya: 5" in prepare for segue print desired value.
The destination VC:
class EmojiVC: UIViewController {
#IBOutlet weak var mainImg: UIImageView!
#IBOutlet weak var emojiImageView: UIImageView!
var imageData: Data!
var imageItself: UIImage!
var emojiImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
print("Zhenya:3 - \(imageData)")
print("Zhenya:4 - \(emojiImage)")
}
override func viewDidAppear(_ animated: Bool) {
print("Zhenya:3 - \(imageData)")
print("Zhenya:4 - \(emojiImage)")
if imageData != nil {
print("Zhenya:2 - \(imageData)")
let img = UIImage(data: imageData)
mainImg.image = img
} else if imageItself != nil {
mainImg.image = imageItself
}
if emojiImage != nil {
print("Zhenya: \(emojiImage)")
emojiImageView.image = emojiImage
}
}
#IBAction func addEmoji(_ sender: Any) {
let img = mainImg.image
performSegue(withIdentifier: "EmojiCollectionVC", sender: img)
}
}
Both prints Zhenya: 3 and Zhenya: 4 print nil. Data that was set in prepare for segue wasn't passed to them.
Segue backToEmojiVC is performed, but data isn't passed.
I did check - if there are any other segues with same identifiers.
I suspect, that value gets destroyed somewhere or somehow when destination VC appears. Or both imageData and emojiImage are re-initialized with nil values.
What might be the problem?

Went through the code step by step. Copy pasted names of segues instead of typing them. Shit started to work.

Related

Why getting unexpected nil value passing data already loaded in tableview by a segue

well my problem is that idk how to pass the info values from the uitableviewcell to the anotherviewcontroller by the segue, could you please help, im unwraped the value, cause the data is already loaded idk what to do to pass the info to the popup controller without crashed
Here is my class model where i set the values
class MovieCell: UITableViewCell {
//
// MARK: - Class Constants
//
static let identifier = "MovieCell"
let urlImage = "https://image.tmdb.org/t/p/w500"
//
// MARK: - IBOutlets
//
#IBOutlet weak var title: UILabel!
#IBOutlet weak var rating: RatingView!
#IBOutlet weak var releaseDate: UILabel!
#IBOutlet weak var poster: UIImageView!
var titlePopUp: String = ""
func configure(movieDictionary: [String: Any]) {
title.text = (movieDictionary["title"] as! String)
titlePopUp = movieDictionary["title"] as! String
releaseDate.text = (movieDictionary["release_date"] as! String)
do {
let url = URL(string: "\(self.urlImage)" + "\(movieDictionary["backdrop_path"]!)")
let data = try Data(contentsOf: url!)
self.poster!.image = UIImage(data: data)
}
catch{
print(error)
}
}
}
heres is the viewcontroller where i get the error in the line 53
class ViewController: UIViewController,UITableViewDataSource, UICollectionViewDataSource, UITableViewDelegate {
var jsonArray: [Any] = []
let movieService = MovieService()
let popUpVC = popUpViewController()
#IBOutlet weak var moviesTableView: UITableView!
#IBOutlet weak var postersView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
movieService.fetchMovies { jsonArray in
if let jsonArray = jsonArray {
self.jsonArray = jsonArray
self.moviesTableView.reloadData()
self.postersView.reloadData()
}
}
self.moviesTableView.delegate = self
self.moviesTableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.jsonArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MovieCell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath) as! MovieCell
cell.configure(movieDictionary: jsonArray[indexPath.row] as! [String: Any])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
present(popUpVC, animated: true, completion: nil)
moviesTableView.deselectRow(at: indexPath, animated: true)
self.performSegue(withIdentifier: "popUp", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popUp" {
let destinationVC = segue.destination as! popUpViewController
let data = MovieCell()
destinationVC.movieN = data.title.text!
}
}
}
and the popup controller is this one
class popUpViewController: UIViewController {
#IBOutlet weak var poster: UIImageView!
#IBOutlet weak var movieName: UILabel!
#IBOutlet weak var releaseDate: UILabel!
#IBOutlet weak var descriptionMovie: UILabel!
var movieN = String()
override func viewDidLoad() {
super.viewDidLoad()
movieName.text = movieN
// Do any additional setup after loading the view.
}
#IBAction func closePop(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
im getting crash passing the label and image, the clasical error, unexpected nil value, but idk why the data is already loaded in the tableview main screen
You're not getting the data from the cell is because in the prepare method, you're creating a new instance of the cell.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popUp" {
let destinationVC = segue.destination as! popUpViewController
let data = MovieCell() // This is where the issue is
destinationVC.movieN = data.title.text!
}
}
The newly created cell instance has no relationship with the one that's already displaying the data. In the new cell, all the properties are either empty or nil. That's why you're getting the 'unexpected nil value' error.
To get that cell's values, you should get a reference to that cell.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) as? MovieCell else { return }
performSegue(withIdentifier: "popUp", sender: cell.titlePopUp)
}
Here you get the cell the user taps on and casts it to the MovieCell to access its titlePopUp property.
The you pass it to the prepare method in the sender parameter.
Finally in the prepare method, you cast it back to String (because the sender parameter is of type Any) and pass it to the popup view.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popUp" {
let destinationVC = segue.destination as! popUpViewController
destinationVC.movieN = sender as? String
}
}
A few final notes: This way of passing around JSON as dictionaries will get the job done but will cause you massive headaches for you down the line if you have to change/maintain your code. What if later you have to pass another value of a movie along with the title to the popup? It's going to become harder and harder to scale this.
Do look in to object oriented way of programming. For example, create a class/struct called Movie to hold all the data (title, release date, rating etc). In the movie service, parse your JSON and create instances of that object and pass them up to the view controller.
i already solved sender was: cell not self
let cell = tableView.cellForRow(at: indexPath) as! MovieCell
self.performSegue(withIdentifier: "popUp", sender: cell)
and the data passed in the segue
if segue.identifier == "popUp" {
if let cell = sender as? MovieCell{
let destinationVC = segue.destination as! popUpViewController
destinationVC.movieN = cell.title.text!
let url = cell.urlImage + cell.posterPath
destinationVC.posterUrl = url
destinationVC.descriptionText = cell.descriptionMovie
destinationVC.releaseDateText = cell.releaseDate.text!
}
}

How to push data from textField to label in another VC

I have mainVC with 4 labels and 4 goToVC buttons, each button use segue to present another 1 of 4 VC. Every vc have textField and doneButton. I want to show text from textField in mainVC labels and i want to use delegates. But instead i got empty labels. Please help.
Delegate.swift
protocol TextFieldDelegate {
func didFinishTextField(text: String)
}
MainVC.swift
class MainViewController: UIViewController, TextFieldDelegate {
#IBOutlet weak var redText: UILabel!
#IBOutlet weak var orangeText: UILabel!
#IBOutlet weak var pinkText: UILabel!
#IBOutlet weak var purpleText: UILabel!
var choosenLabel: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toRedVC" {
let vc = segue.destination as! RedViewController
vc.delegate = self
choosenLabel = redText
} else if segue.identifier == "toOrangeVC" {
let vc = segue.destination as! OrangeViewController
vc.delegate = self
choosenLabel = orangeText
} else if segue.identifier == "toPinkVC" {
let vc = segue.destination as! PinkViewController
vc.delegate = self
choosenLabel = pinkText
} else if segue.identifier == "toPurpleVC" {
let vc = segue.destination as! PurpleViewController
vc.delegate = self
choosenLabel = purpleText
}
}
func didFinishTextField(text: String) {
if let data = choosenLabel {
data.text = text
}
}
#IBAction func redButton(_ sender: UIButton) {
}
#IBAction func orangeButton(_ sender: UIButton) {
}
#IBAction func pinkButton(_ sender: UIButton) {
}
#IBAction func purpleButton(_ sender: UIButton) {
}
}
RedVC(other 3 same).swift
class RedViewController: UIViewController {
var delegate: TextFieldDelegate?
var textVar: String = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func textField(_ sender: UITextField) {
if let data = sender.text {
textVar = data
}
}
#IBAction func doneButton(_ sender: UIButton) {
delegate?.didFinishTextField(text: textVar)
dismiss(animated: true, completion: nil)
}
}
Everything seems ok in your code. I suggest you to check your text field's actions are set to editingChanged in ViewController.

Swift how to get cell from prepare for segue in collectionView

Im using autolayout and for the life of me cannot set the proceeding view controller image view with the previous view controller collection view cell's image. Im using
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "openDetailView", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openDetailView" {
let cell = sender as? ImageCollectionViewCell
let detailVC = segue.destination as! DetailedImageViewController
detailVC.imageToPresent = cell?.imageView.image
}
}
This is the class that I have already set up to receive the image
class DetailedImageViewController : UIViewController{
var imageToPresent: UIImage!
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = imageToPresent
}
}
The segue performs as expected but the image DOES NOT show in detailedImageViewController. Thanks in advance.
You need to set the imageToPresent into a UIImageview in order to show the image.
class DetailedImageViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var imageToPresent : UIImage!
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = imageToPresent
}
...
It's not a best practice to avoid optional unwrapping in this case. Try to use this code and check the errors if they happen.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "openDetailView" {
guard let cell = sender as? ImageCollectionViewCell else {
assertionFailure("Failed to unwrap sender. Try to set a breakpoint here and check what sender is")
return
}
let detailVC = segue.destination as! DetailedImageViewController
guard let cellImage = cell.imageView.image else {
assertionFailure("The cell has no image in image view")
return
}
detailVC.imageToPresent = cellImage
}
}

Button from UITableViewController sends data to detailViewController - swift

I have a problem that I cannot wrap my head around.. You cannot create a button action in a UITableViewController.. So I tried to just control + drag from the button to the detailtableViewController and pressed push.. But when I use prepareForSegue and I then click on the button it should send the button text to a string in the detailtableViewController, but sometimes it's not the correct name, because there are multiple cells in the tableView and the name is not always the same..
What I need it to do is, when you click the button "Button:
It should go to this detailtableViewController:
With the name that is set as text to the Button.
The variable that should receive the name of the button is called viaSegue and it is a string.
My UITableViewController:
class feedTableViewController: UITableViewController, PostCellDelegate {
#IBOutlet weak var loadingSpinner: UIActivityIndicatorView!
#IBOutlet weak var profilePicture: UIImageView!
var sendName = "No name"
var facebookProfileUrl = ""
var dbRef: FIRDatabaseReference!
var updates = [Sweet]()
var gottenUserId : Bool? = false
var gottenUserIdWorkout : Bool? = false
override func viewDidLoad() {
super.viewDidLoad()
let logoImage = UIImageView(frame: CGRect(x:0, y:0, width: 60, height: 32))
logoImage.contentMode = .ScaleAspectFit
let logo = UIImage(named: "logo.png")
logoImage.image = logo
self.navigationItem.titleView = logoImage
loadingSpinner.startAnimating()
if let user = FIRAuth.auth()?.currentUser {
let userId = user.uid
let storage = FIRStorage.storage()
// Refer to your own Firebase storage
let storageRef = storage.referenceForURL("**********")
let profilePicRef = storageRef.child(userId+"/profile_pic.jpg")
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
profilePicRef.dataWithMaxSize(1 * 300 * 300) { (data, error) -> Void in
if (error != nil) {
// Uh-oh, an error occurred!
print("Unable to download image")
} else {
// Data for "images/island.jpg" is returned
// ... let islandImage: UIImage! = UIImage(data: data!)
if (data != nil){
self.profilePicture.image = UIImage(data: data!)
self.profilePicture.layer.cornerRadius = self.profilePicture.frame.size.width/2
self.profilePicture.clipsToBounds = true
}
}
}
}
dbRef = FIRDatabase.database().reference().child("feed-items")
startObersvingDB()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 205
}
func startObersvingDB() {
FIRDatabase.database().reference().child("feed-items").queryOrderedByChild("date").observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
var newUpdates = [Sweet]()
for update in snapshot.children {
let updateObject = Sweet(snapshot: update as! FIRDataSnapshot)
newUpdates.append(updateObject)
}
self.updates = newUpdates.reverse()
self.tableView.reloadData()
}) { (error: NSError) in
print(error.description)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return updates.count
}
protocol PostCellDelegate: class {
func postCell(postCell: PostCell, didTouchUpInside button: UIButton)
}
func postCell(postCell: PostCell, didTouchUpInside button: UIButton) {
let identifier = "toDetailtableViewController"
let username = postCell.nameButton.titleLabel?.text
performSegue(withIdentifier: identifier, sender: username)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Lots of stuff happening here
My custom cell:
class updateTableViewCell: UITableViewCell {
#IBOutlet weak var updateLabel: UILabel!
#IBOutlet weak var picView: UIImageView!
#IBOutlet weak var likesLabel: UILabel!
#IBOutlet weak var likeButton: UIButton!
#IBOutlet weak var hand: UIImageView!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var nameButton: UIButton!
weak var delegate: PostCellDelegate?
var pathDB : String!
var dbRef: FIRDatabaseReference!
var gottenUserId : Bool? = false
var sendNameCell = "No name here"
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func likeTapped(sender: AnyObject) {
//print(pathDB)
checkClickOnLikeButton()
}
#IBAction func didTouchUpInsideButton(sender: AnyObject) {
delegate?.postCell(self, didTouchUpInside: button)
}
func checkClickOnLikeButton() {
let dataPathen = self.pathDB
// print(dataPathen)
if let user = FIRAuth.auth()?.currentUser {
let userId = user.uid
FIRDatabase.database().reference().child("feed-items").child(dataPathen).child("likesForPost").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// Get user value
self.gottenUserId = snapshot.value![userId] as? Bool
// print(self.gottenUserId)
if self.gottenUserId == true {
print("Der er trykket high five før")
FIRDatabase.database().reference().child("feed-items").child(dataPathen).child("likesForPost").child(userId).removeValue()
let greyButtonColor = UIColor(red: 85/255, green: 85/255, blue: 85/255, alpha: 1.0)
self.likeButton.setTitleColor(greyButtonColor, forState: UIControlState.Normal)
self.hand.image = UIImage(named: "high.png")
} else {
print("Der er IKKE trykket like før")
let quoteString = [userId: true]
FIRDatabase.database().reference().child("feed-items").child(dataPathen).child("likesForPost").updateChildValues(quoteString)
let blueButtonColor = UIColor(red: 231/255, green: 45/255, blue: 60/255, alpha: 1.0)
self.likeButton.setTitleColor(blueButtonColor, forState: UIControlState.Normal)
self.hand.image = UIImage(named: "highfive.png")
}
// ...
}) { (error) in
print(error.localizedDescription)
}
}
}
}
Assuming you have already created a custom class for the cell containing the Button, you must create an #IBAction for the didTouchUpInside event. You must also create a segue directly from the UITableViewController to the detailtableViewController (so not from a button or a view, from one view controller to the other). You will need to give this segue an identifier since we're going to be performing it manually.
Once you've hooked up the #IBAction in the cell, we need a way of performing the segue from the cell. To do this, we need a reference to the UITableViewController. We could get it using delegates or maybe responders, recently I've been using responders.
Delegate
Create a protocol for your UITableViewController to conform to.
protocol PostCellDelegate: class {
func postCell(_ postCell: PostCell, didTouchUpInside button: UIButton)
}
Create a delegate variable in your custom cell class call it's didTouchUpInside method from button's #IBAction for that event.
weak var delegate: PostCellDelegate?
#IBAction func didTouchUpInsideButton() {
delegate?.postCell(self, didTouchUpInside: button)
}
Now in your UITableViewController, you must conform to the delegate and set the delegate of the cells in the cellForRowAt method.
class tableViewController: UITableViewController, PostCellDelegate {
//...
// MARK: PostCellDelegate
func postCell(_ postCell: PostCell, didTouchUpInside button: UIButton) {
let identifier = "toDetailtableViewController"
let username = postCell.button.titleLabel?.text
performSegue(withIdentifier: identifier, sender: username)
}
//...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.delegate = self
return cell
}
//...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch (segue.destination, sender) {
case let (controller as detailtableViewController, username as String):
controller.usernameTextField.text = username
break
default:
break
}
}
}

How do I assign image and label values from NSDictionary?

I am passing data(image and Label) from one view controller to another. I am storing them in a NSDictionary, and the data is being passed to the new viewcontroller. I have connected a image view as well as a label. How do I assign the image and the label the values in the NSDictionary? Below is the code for transferring the data from one view controller to another.
var dicSelected : NSDictionary!
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("Cell \(indexPath.item) selected")
print(arrayOfFriendsNames[indexPath.item])
self.dicSelected = ["friendname" : arrayOfFriendsNames[indexPath.item], "friendimage" : arrayOfFriends[indexPath.item]]
self.performSegueWithIdentifier("friendaccess", sender: dicSelected)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "friendaccess"){
let nextViewOBJ = segue.destinationViewController as! FriendProfilePages
nextViewOBJ.dicData = self.dicSelected;
}
}
Below is the code for the FriendProfilePages.
#IBOutlet weak var friendimage: UIImageView!
#IBOutlet weak var friendname: UILabel!
var dicData : NSDictionary?
override func viewWillAppear(animated: Bool) {
print("Dictionary: \(self.dicData)")
}
Or you can create a model class with properties
var image: UIImage?
var text: String?
and then pass that class's object to next view controller and assign those values to your friendimage.image and friendname.text
Code for FriendProfilePages:
#IBOutlet weak var friendimage: UIImageView!
#IBOutlet weak var friendname: UILabel!
var dataModel : DataModel?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
friendimage.image = dataModel.image
friendname.text = dataModel.text
}
Now For performing Segue
var selectedData = DataModel()
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("Cell \(indexPath.item) selected")
print(arrayOfFriendsNames[indexPath.item])
self.selectedData.text = arrayOfFriendsNames[index] as? String
self.selectedData.image = arrayOfFriends[index] as? UIImage
self.performSegueWithIdentifier("friendaccess", sender: dicSelected)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "friendaccess"){
let nextViewOBJ = segue.destinationViewController as! FriendProfilePages
nextViewOBJ.dataModel = self.selectedData;
}
}
Declare it anywhere
class DataModel : NSObject {
var image: UIImage?
var text: String?
}
just to get you an idea...
You should use proper types to transfer data rather than dictionaries. You could define an appropriate type like this
struct FriendProfileData {
let name: String
let image: UIImage
}
You'll need to update your selection code to create one of these new structs like this
var selected: FriendProfileData
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selected = FriendProfileData(name: arrayOfFriendsNames[indexPath.item], image: arrayOfFriends[indexPath.item])
performSegueWithIdentifier("friendaccess", sender: self)
}
Your prepareForSegue needs to then change to
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "friendaccess"){
guard let selected = selected else {
return
}
let nextViewOBJ = segue.destinationViewController as! FriendProfilePages
nextViewOBJ.dicData = selected;
}
}
Then in the viewDidAppear
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
friendImage.image = selected.image
friendName.text = selected.name
}

Resources