adams.s adams.s - 6 months ago 123
Swift Question

Retrieving firebase data of a tableview cell to a view controller's label

I have like a social app with a sort of newsfeed. if u click on the users name from a post in the newsfeed, you will go to his profile. Now i can't retrieve the data from that specific cell/post to the other viewController.
so i have to display the user's profile, with he's username, etc. but that doesn't work?

i have a Post model:

class Post {
private var _postDescription: String!
private var _profileImageURL: String?
private var _likes: Int!
private var _username: String!
private var _postKey: String!
private var _timeStamp: String!
private var _postRef: Firebase!

var postDescription: String? {
return _postDescription
}

var likes: Int {
return _likes
}

var username: String {
return _username
}

var postKey: String {
return _postKey
}

var profileImageURL: String? {
return _profileImageURL
}

init(description: String, username: String, profileImageURL: String?) {
self._postDescription = description
self._username = username
self._profileImageURL = profileImageURL
}

init(postKey: String, dictionary: Dictionary<String, AnyObject>) {
self._postKey = postKey

if let likes = dictionary["likes"] as? Int {
self._likes = likes
}

if let desc = dictionary ["description"] as? String {
self._postDescription = desc
}

if let imgUrl = dictionary["profileImg"] as? String {
self._profileImageURL = imgUrl
}

if let user = dictionary ["username"] as? String {
self._username = user
} else {
self._username = ""
}

self._postRef = DataService.ds.REF_POST.childByAppendingPath(self._postKey)
}

}


this is my profileVC:

class ProfileVC: UIViewController {

@IBOutlet weak var username: UILabel!

var post: Post?

override func viewDidLoad() {
super.viewDidLoad()

username.text = post.username // gives me a nil error.
}
}


and i use a TapGestureRecognizer in my tableViewCell to perform the segue.
in my cellForRowAtIndexPath:

let profileLblTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(NewsVC.goToProfileScreen(_:)))
profileLblTapRecognizer.numberOfTapsRequired = 1
profileLblTapRecognizer.delegate = self

cell.usernameLabel.tag = indexPath.row
cell.usernameLabel.userInteractionEnabled = true
cell.usernameLabel.addGestureRecognizer(profileLblTapRecognizer)


and the goToProfileScreen function:

func goToProfileScreen(gesture: UITapGestureRecognizer) {
self.performSegueWithIdentifier("ProfileScreen", sender: self)

}


this is my datamodel on firebase:
enter image description here

UPDATE:

i tried this instead:

let profileLblTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(NewsVC.prepareForSegue(_:sender:)))


with this function:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ProfileScreen" {
if let cell = sender as? NewsCell, row = tableView.indexPathForCell(cell)?.row, vc = segue.destinationViewController as? ProfileVC {
vc.post = posts[row]

}

}


}


but that gave me an error on appDelegate: Thread 1: EXC_BAD_ACCESS(code=1, address = 0x1)

Answer

I've added this as an answer rather than a comment so that I can add and format some code examples.

When you call performSegueWithIdentifier, a NEW instance of the view controller identified by that segue is created, so all of its properties will be their defaults.

You have two ways of instantiating this view controller and setting properties before it loads. The first is the prepareForSegue option, in your case it may look something like this:

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    if (segue.identifier == "ProfileScreen") {
        let vc = segue.destinationViewController as! ProfileVC
        vc.post = post
    }
}

Another option is to create and present the view controller yourself, this example uses a storyboardID

let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewControllerWithIdentifier("profileVC") as! ProfileVC
vc.post = post

presentViewController(vc, animated: false, completion: nil)

Update:

I'm not sure why you are adding a tap gesture recogniser to this, you could just use didSelectRowAtIndexPath, have a look at this other question and answer

you could have a property on your table view controller called selectedItem or something similar. and then in didSelectRowAtIndexPath set selectedItem to the item at the current index. Then in prepare for segue you would just do vc.post = selectedItem

Update Two:

After the op sharing their code privately, I noticed that the issue is that the user is using tapGestureRecogniser in the tableView. I added some code into the called function to get the row in which contained the tapped view, once I had the indexPath it was then easy to store it in a temporary property and retreive later in the prepareForSegue method, details below

// temp property
var selectedPost:Post?

// function called on tap
func viewProfile(sender:UITapGestureRecognizer) {

    if (sender.state == UIGestureRecognizerState.Ended) {
        let point = sender.locationInView(self.tableView)
        if let indexPath = self.tableView.indexPathForRowAtPoint(point) {
            selectedPost = self.posts[indexPath.row]
            performSegueWithIdentifier("ProfileScreen", sender: self)
        }
    }
}

// Prepare for segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    if (segue.identifier == "ProfileScreen") {
        let vc = segue.destinationViewController as! ProfileVC
        if let post = selectedPost {
            vc.post = post
        }
    }
}
Comments