passing data with performSegueWithIdentifier nil - ios

I have the segue setup as:
and the tableView row selection:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
// Ensure controller knows which dataset to pull from,
// so detail view is correct
var friendChat: Friend!
if searchController.active && searchController.searchBar.text != "" {
friendChat = filterMappedFriends[indexPath.row]
} else {
friendChat = mappedFriends[indexPath.row]
}
// Now set the conditional cases: if a friend then chat, if user then friend request if not user then can invite them:
if(friendChat.statusSort == 2) {
self.performSegueWithIdentifier("showIndividualChat", sender: friendChat)
} else if (friendChat.statusSort == 1) {
print("Can invite to be friend")
} else if (friendChat.statusSort == 0) {
print("Invite to Feast")
}
}
and the prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let indexPath = tableView.indexPathForSelectedRow {
// Ensure controller knows which dataset to pull from,
// so detail view is correct
let friendChat: Friend
if searchController.active && searchController.searchBar.text != "" {
friendChat = filterMappedFriends[indexPath.row]
} else {
friendChat = mappedFriends[indexPath.row]
}
// Now set the conditional cases: if a friend then chat, if user then friend request if not user then can invite them:
if segue.identifier == "showIndividualChat" {
let controller = segue.destinationViewController as! IndividualChatController
controller.friendChat = friendChat
controller.senderId = Global.sharedInstance.userID
controller.senderDisplayName = Global.sharedInstance.userName
}
}
}
However, the object friendChat, seen in controller.friendChat, of the destination controller is always nil.
How can I pass the data:
controller.friendChat = friendChat
controller.senderId = Global.sharedInstance.userID
controller.senderDisplayName = Global.sharedInstance.userName
to the destination controller successfully?

The first thing you are doing in didSelectRowAtIndexPath is deselecting the row, so when you try and access the selected row in prepareForSegue you are going to get no row selected.
Since you are passing the Friend instance as your sender to performSegueWithIdentifier you can just say let friendChat = sender as? Friend in prepareForSegue;
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
if segue.identifier == "showIndividualChat" {
if let friendChat = sender as? Friend {
let controller = segue.destinationViewController as! IndividualChatController
controller.friendChat = friendChat
controller.senderId = Global.sharedInstance.userID
controller.senderDisplayName = Global.sharedInstance.userName
}
}
}
For Swift 3
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
if segue.identifier == "showIndividualChat" {
if let friendChat = sender as? Friend {
let controller = segue.destination as! IndividualChatController
controller.friendChat = friendChat
controller.senderId = Global.sharedInstance.userID
controller.senderDisplayName = Global.sharedInstance.userName
}
}
}

Related

How to refresh DetailViewController when MasterViewController is updated

I have an application where the users can update and add tasks. But when the users update the task I need to update the details in the DetailViewController
When the users updates the task and saves the data in MasterViewController saves but the data in DetailViewController stays the same
How can I fix it so that the details get updated as well
The tasks are added and edited using the AddProjectController
I tried the following methods but none if them worked, all caused the the application to crash
let vc = DetailViewController()
let row = vc.view.layoutIfNeeded();
//Second method
let vc = MasterViewController()
vc.performSegue(withIdentifier: "showDetail", sender: vc.self)
The data is set in the DetailViewController using
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
lblName.text = detail.name ?? ""
lblCode.text = detail.code ?? ""
}
MasterViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let object = fetchedResultsController.object(at: indexPath)
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
controller.details = object;
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
detailViewController = controller
}
}
}
EditViewController
func editEvents(_ sender: Any){
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Proj")
do {
let test = try context.fetch(fetchRequest)
var objectUpdate = test[0] as! Assesment;
objectUpdate.name = txtname.text ?? ""
objectUpdate.code = txtCode.text ?? "";
do {
try context.save()
}
catch {
print(error)
}
}
catch {
print(error)
}
}
I want to update the details when the editEvent function is finished executing
Is there anyway I can get this to work?
If You are using coreData .. You can implement FRC in details .. so you get refreshed data every time in callback ...
In your MasterViewController define a variable
Here is another workaround
private var controller : DetailViewController? = nil
Then in your Segue method use this controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let object = fetchedResultsController.object(at: indexPath)
self.controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController // here you set your instence variable controller
controller.details = object;
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
detailViewController = controller
}
}
}
func editEvents(_ sender: Any){
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Proj")
do {
let test = try context.fetch(fetchRequest)
var objectUpdate = test[0] as! Assesment;
objectUpdate.name = txtname.text ?? ""
objectUpdate.code = txtCode.text ?? "";
do {
try context.save()
if let detailsCotrollerExists = controller {
detailsCotrollerExists.updateDataWithObject(objectUpdate) // function you will write in detail class which takes object and refresh data there .. Either outlets or Table/Collection View reload
}
}
catch {
print(error)
}
}
catch {
print(error)
}
}
Then you write a function in DetailViewController func updateDataWithObject(_ obj: Assesment) function you will write in detail class which takes object and refresh data there .. Either outlets or Table/Collection View reload
Write function in DetailViewController
func updateDataWithObject(_ obj: Assesment) {
// Refresh data
}

swift ios how to access data from a collection view

I have code below in a collection view and its result, what I want to know is how to access those data from my collection view to my function prepare. I want to get the value of getTempDetails like
getTempDetails["name"] as! String
to be the value of my
destination.chore_name = getTempDetails["name"] as! String
but I can't seem to access it. thanks
code for prepare
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let distanation = segue.destination as? ChoreDetailsViewController {
distanation.chore_name = "Hi"
}
}
code for
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
// print("You selected cell #\(indexPath.item)!")
// print("emong gepili:" , titleArray[indexPath.row])
if indexPath.row == 0 {
performSegue(withIdentifier: "goToSegue", sender: nil)
} else {
performSegue(withIdentifier: "gotoDetails", sender: nil)
print("You selected cell #\(indexPath.item)!")
if let getTempDetails: [String : Any] = getAllDetail[indexPath.row] {
print("You selected ID #\( getTempDetails["reward"] as? String ?? "" )!")
print("You selected ID #\( getTempDetails["desc"] as? String ?? "" )!")
print("You selected ID #\( getTempDetails["sched"] as? String ?? "" )!")
Possibly you can use:
var strReward:String = String()
var strDesc:String = String()
var sreSched:String = String()
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0 {
performSegue(withIdentifier: "goToSegue", sender: nil)
} else {
performSegue(withIdentifier: "gotoDetails", sender: nil)
print("You selected cell #\(indexPath.item)!")
if let getTempDetails: [String : Any] = getAllDetail[indexPath.row] {
strReward = (getTempDetails["reward"] as? String)!
strDesc = (getTempDetails["desc"] as? String)!
sreSched = (getTempDetails["sched"] as? String)!
}
}
}
Pass this Data to next VC or can use them in any way you want
Note:- I am not sure what data type is the data coming, I have assumed it to be a String type. You can use accordingly
use the keyword of indexPathsForSelectedItems for colletionview
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "gotoDetails" ,
let nextScene = segue.destination as? ChoreDetailsViewController ,
let indexPath = self.yourCollectionViewName. indexPathsForSelectedItems().first {
if let getTempDetails: [String : Any] = getAllDetail[indexPath.item] {
print("You selected ID #\( getTempDetails["reward"] as? String ?? "" )!")
print("You selected ID #\( getTempDetails["desc"] as? String ?? "" )!")
print("You selected ID #\( getTempDetails["sched"] as? String ?? "" )!")
nextScene.chore_name = getTempDetails["name"] as! String
}
}
}
You can store last selected items data in class var
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0 {
performSegue(withIdentifier: "goToSegue", sender: nil)
} else {
if let getTempDetails: [String : Any] = getAllDetail[indexPath.row],
let name = getTempDetails["name"] as? String {
selectedChoreName = name
performSegue(withIdentifier: "gotoDetails", sender: nil)
}
and then
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let distanation = segue.destination as? ChoreDetailsViewController {
distanation.chore_name = selectedChoreName
selectedChoreName = "" //optional
}
}

Segue using tableViewCells

I am going to try to be as clear as I can be on this question but if you need more information please please ask. I have a tableviewcontroller that has a list of all the messages the logged in user has had with other users of the app. When clicked the logged in user clicks a cell, I would like for the user to segue to a view controller that allows them to chat with whatever user they like. This chat was acquired using JSQMessageController. However, when I set it the segue in the tableviewcontroller, show below:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let message = messages[indexPath.row]
if message.ReceiverId != self.loggedInUserUid {
var newVariable = message.ReceiverId
if self.userpicuid == newVariable {
let ref = FIRDatabase.database().reference().child("users").child(userpicuid!)
ref.observeSingleEvent(of: .value, with: { (snapshot)
in
if let dictionary = snapshot.value as? [String: AnyObject]{
for post in dictionary {
let messages = post.value as! [String: AnyObject]
for (id, value) in messages {
self.username = messages["username"] as? String
}}}})}} else if message.senderId != self.loggedInUserUid {
let newVariable = message.senderId
if self.userpicuid == newVariable {
let ref = FIRDatabase.database().reference().child("users").child(userpicuid!)
ref.observeSingleEvent(of: .value, with: { (snapshot)
in
if let dictionary = snapshot.value as? [String: AnyObject]{
for post in dictionary {
let messages = post.value as! [String: AnyObject]
for (id, value) in messages {
self.username = messages["username"] as? String
}}}})}
}
performSegue(withIdentifier: "MessageNow", sender: self.userpicuid)
}
override public func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "MessageNow", let chatVc = segue.destination as? SendMessageViewController else {
return
}
chatVc.senderId = self.loggedInUser?.uid
chatVc.receiverData = sender as AnyObject
chatVc.senderDisplayName = self.userpicuid
chatVc.username = self.username
}
I get an error in the MessageViewController:
var receiverData: AnyObject?
override func viewDidLoad() {
super.viewDidLoad()
let receiverId = receiverData as! String
let receiverIdFive = String(receiverId.characters.prefix(5))
let senderIdFive = String(senderId.characters.prefix(5))
if (senderIdFive > receiverIdFive)
{
self.convoId = senderIdFive + receiverIdFive
}
else
{
self.convoId = receiverIdFive + senderIdFive
}}
I get the error on the let receiverId = receiverData as! String that:
Could not cast value of type 'Chat_App.MessageTableViewCell' (0x10eb2ef10) to 'NSString' (0x110ab1c60).
in a different view controller, I have:
#IBAction func sendMessage(_ sender: Any) {
performSegue(withIdentifier: "sendMessageToUser", sender: self.userpicuid)
}
override public func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "sendMessageToUser", let chatVc = segue.destination as? SendMessageViewController else {
return
}
chatVc.senderId = self.loggedInUser?.uid
chatVc.receiverData = sender as! String!
chatVc.senderDisplayName = self.userpicuid
chatVc.username = self.username
}
And it segues perfectly.
Sender is Any? and you're casting it to AnyObject. AnyObject refers to a class type, and it's asserting when you attempt to cast it to a Swift value type (String).
Try this instead:
chatVc.receiverData = NSString(string: sender as! String)
Your sender is the the UITableViewCell that initiated the segue. You are crashing when casting it to NSString. Remove this line
let receiverId = receiverData as! String
In prepareForSegue do this instead
chatVc.receiverData = self.userpicuid

Perform segues when I want

Evening, I have 2 show segues in my VC. But I want to fires these segues only when my pickerView has a row.count > 0.
This is what I have:
Override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "searchImages" {
if pickerView.numberOfRows(inComponent: 0) > 0 {
let controller = (segue.destination) as! WebViewController
//replacing " " with "+" for google search queries
let type: WebViewController.SearchType = .image
let queryString = String(nameLabel.text!.characters.map {
$0 == " " ? "+" : $0
})
controller.searchType = type
controller.queryString = queryString
print("2")
}
}
if segue.identifier == "searchWiki" {
if pickerView.numberOfRows(inComponent: 0) > 0 {
let controller = (segue.destination) as! WebViewController
//replacing " " with "+" for google search queries
let type: WebViewController.SearchType = .wiki
let queryString = String(nameLabel.text!.characters.map {
$0 == " " ? "+" : $0
})
controller.searchType = type
controller.queryString = queryString
}
}
}
I know I should use: ShouldperformSomething. But I don't know hot use it.
Any tips?
You should implement shouldPerformSegue instead of checking pickerView.numberOfRows(inComponent: 0) > 0 in your prepareForSegue method.
P.S: Swift 3 Code (I assume that this is what you want).
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return pickerView.numberOfRows(inComponent: 0) > 0
}
Now, prepareForSegue method should be similar to:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "searchImages" {
let controller = (segue.destination) as! WebViewController
//replacing " " with "+" for google search queries
let type: WebViewController.SearchType = .image
let queryString = String(nameLabel.text!.characters.map {
$0 == " " ? "+" : $0
})
controller.searchType = type
controller.queryString = queryString
print("2")
}
if segue.identifier == "searchWiki" {
let controller = (segue.destination) as! WebViewController
//replacing " " with "+" for google search queries
let type: WebViewController.SearchType = .wiki
let queryString = String(nameLabel.text!.characters.map {
$0 == " " ? "+" : $0
})
controller.searchType = type
}
}
Hope this helped.
You shouldn't be doing the check on the prepare delegate of the UIStoryboardSegue, but on the shouldPerform:
override func shouldPerformSegueWithIdentifier(identifier: String!, sender: AnyObject!) -> Bool {
return pickerView.numberOfRows(inComponent: 0) > 0
}

Ambiguous use of 'numberOfPages'

I don't understand what is ambiguous about my use of book.numberOfPages in my TableViewController. I'm not having any problems with using any of the other attributes. There shouldn't be any ambiguity here: I have defined it as a Float in the data model editor and at the top of the TableViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showBookDetail" {
let indexPath:NSIndexPath = self.tableView.indexPathForSelectedRow()!
let book: AnyObject = addBookViewController.fetchedResultsController.objectAtIndexPath(indexPath) as! Book
var detailViewController:BookDetailViewController = segue.destinationViewController as! BookDetailViewController
detailViewController.title = book.name
detailViewController.pagesReadText = "\(Int(book.pagesRead))"
detailViewController.timeReadText = "\(Int(book.timeRead))"
println(book.numberOfPages)
}
}
Probably you can't declare book as AnyObject so the compiler knows that it is a Book...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showBookDetail" {
let indexPath:NSIndexPath = self.tableView.indexPathForSelectedRow()!
let book = addBookViewController.fetchedResultsController.objectAtIndexPath(indexPath) as! Book
var detailViewController:BookDetailViewController = segue.destinationViewController as! BookDetailViewController
detailViewController.title = book.name
detailViewController.pagesReadText = "\(Int(book.pagesRead))"
detailViewController.timeReadText = "\(Int(book.timeRead))"
println(book.numberOfPages)
}
}

Resources