Segue to chat viewcontroller - ios

I have included images to hopefully make this easier to understand. My FirstViewController has a collection view with a list of users from my firebase database.When I click on the users I am segued to a DetailedViewController that has more information about the user that was clicked. Within that viewController, the goal is to click on the compose button and segue to a viewController that allows me to chat with the user, whose profile I clicked on.
I have gotten as far as this for segueing from DetailedViewController to Message user 1.
#IBAction func SendMessage(_ sender: Any) {
performSegue(withIdentifier: "chat", sender: self)
}
I am not sure how to make sure I am sending the particular user I click on a message.
This is how I am passing data from FirstViewController to DetailedViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "details" {
if let indexPaths = self.CollectionView!.indexPathsForSelectedItems{
let vc = segue.destination as! BookDetailsViewController
let cell = sender as! UICollectionViewCell
let indexPath = self.CollectionView!.indexPath(for: cell)
let post = self.posts[(indexPath?.row)!] as! [String: AnyObject]
let Booked = post["title"] as? String
vc.Booked = Booked
print(indexPath?.row)
} }

One route to take is in your DetailViewController class, or whatever class you have implementing performSegue(withIdentifier:, sender:), add this:
override public func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "chat", let dest = segue.destination as? ChatViewController else {
return
}
dest.user = self.user
}
I don't know how you have the user declared but if user is an object, do what I have above. If you're saying user as a way to umbrella multiple properties you want to pass.. do this:
dest.name = self.name
dest.number = self.number
//etc
prepare(for segue:, sender:) allows you to intercept any segue from it's class and set up whatever variables needed before the performSegue(...) executes. To target your code to a specific segue/destination/situation, make sure your code runs a check on those constraints before executing; otherwise the code will execute on all segues implemented in the class. In my example, I used your segue's identifier as that check.

Related

Data turns Nil when passing from previous View Controller going to another View Controller using Segue

I am about to pass data from a ViewController going to another ViewController using segue. When checking the data(event) from a variable thru breakpoint in the 1st View Controller the data(event) is not nil. But when I checked the 2nd View Controller the data(event) is nil. I am confuse whether if the reason is, I have error in my codes or because of the error appeared in my console that says Unable to insert COPY_SEND. Hope I can get some help from you. Thank you
Segue from First View Controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DashBoardViewController" {
let dashBoardController = segue.destination as! DashBoardViewController
dashBoardController.self.event = event
dashBoardController.self.passcode = passcode
}
}
Event and Passcode Turns Nil
override func viewDidLoad() {
super.viewDidLoad()
guard event != nil, passcode != nil else {
_ = SCLAlertView(appearance: appearance).showError("No Event Details", subTitle: "There's no event details, please logout and try again")
return
}
showEventDetails()
}
showEventDetails
func showEventDetails() {
DispatchQueue.main.async{
self.eventNameLabel.text = "\(self.event.name.uppercased())"
self.locationLabel.text = "\(self.event.location.uppercased())"
if let dateStringFromDate = getFormattedStringFromDate(date: (self.event.startDateTime), formatString: "MMMM dd, yyyy/ hh:mm a") {
self.dateTimeLabel.text = dateStringFromDate
} else {
self.dateTimeLabel.text = "-"
}
}
}
I am assuming you linked the segue which goes to DashBoardViewController on your submitButton by Interface Builder, which means when you are tapping on the submit button, the #IBAction func submitButton(_ sender: UIButton) { } gets called, where you check if your passcode is good to go and if so you are calling validateEventPasscode() which calls an API endpoint (asynchronous) and only there you are populating the self.event = event (line 187 in ViewController.swift).
Now, what really happens is that when you link a segue from a button by IB (interface builder), there will be a perform segue internally which we have to stop by overriding the following method in ViewController.swift: source
func shouldPerformSegue(withIdentifier identifier: String,
sender: Any?) -> Bool {
return false
}
This way your call from line 190 - ViewController.swift:
self.performSegue(withIdentifier: "showEventDashboard", sender: self)
is the one that fires:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DashBoardViewController" {
let dashBoardController = segue.destination as! DashBoardViewController
dashBoardController.event = event
dashBoardController.passcode = passcode
}
}
You can test my theory by placing three breakpoints in ViewController.swift:
line 134 at validateEventPasscode() from submitButton IBAction func;
line 190 at self.performSegue(withIdentifier: "showEventDashboard", sender: self) from validateEventPasscode() func;
line 108 at dashBoardController.event = event from prepare(for segue, sender) func;
Buggy order of execution: 1, 3, 2 - at the moment this I would expect if my theory is correct;
Desired order of execution: 1, 2, 3.
Long story short, you populate your self.event after you perfomSegue and go to the next screen, that's why your event is nil in the next VC.
I used as reference the ViewController.swift file from your repo: ViewController.swift
Hope it helps, cheers!
Replace your prepareForSegue method in FirstViewController with this
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DashBoardViewController" {
let dashBoardController = segue.destination as! DashBoardViewController
dashBoardController.event = event
dashBoardController.passcode = passcode
}
}
you don't need to write
dashBoardController.self.event = event
dashBoardController.self.passcode = passcode
Just remove self from above two lines.

iOS - Sending sender-linked variables through a segue

Through using a loop I've programmatically created a bunch of UIButtons. Each of these triggers the same segue to another View Controller which is supposed to display some information related to the button.
For this to work, I need to send at least one attribute/variable linked to the specific button tapped through the segue.
One option I tried was creating a new UIButton class to hold the attribute.
class statButton: UIButton {
var buttonIndex = Int()
}
If this even works, how would I access that data here:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "StatDetailSegue" {
if let destinationVC = segue.destination as? StatDetailViewController {
destinationVC.statTitle = // need to access here
}
}
}
The variable I need to send is a value in a dictionary
You have to make sure that the sender is a statButton, and then cast its buttonIndex property to a proper String:
if let destinationVC = segue.destination as? StatDetailViewController,
let index = (sender as? statButton).buttonIndex {
destinationVC.statTitle = String(index) //this is supposing that destinationVC.statTitle expects a String
}
P.S: It is a convention in Swift to have the names of classes with an uppercase first letter:
class statButton: UIButton { ... }

Pass variable returned Object via Segue depending on which button pressed

I have a WorkoutGenerator struct which returns different workouts based on different parameters, for example generateWorkout.standardWorkout returns something different to generateWorkout.hardWorkout.
I have 3 buttons on a 'Workout Setup' page, each meant to pass a different type of workout to the 'Workout Page' (there is also an input field for 'workout time').
I have currently have one segue goToWorkout from "Workout Setup Page" to "Workout Page"
What I want to do, is trigger the segue to the workout page, passing the different workouts depending on what the user presses.
So far I have :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//currently have one segue between the pages
if segue.identifier == "goToWorkout" {
let finalTimeForWorkout = Int(timeInputField.text!)
//set a standardWorkout to pass to Workout Page
let finalWorkout = FinalWorkout(generatedWorkout: WorkoutGenerator.standardWorkout.generate(), timeForWorkout: finalTimeForWorkout!)
//set the final parameters ready to pass
let finalWorkoutTime = finalWorkout.timeForWorkout
let finalWorkoutExercises = finalWorkout.generatedWorkout.workoutExercises
if let destVC = segue.destination as? WorkoutController {
destVC.selectedWorkoutExerciseArray = finalWorkoutExercises
destVC.selectedWorkoutTime = finalWorkoutTime
}
}
}
and then something like this for each button :
//use this button to pass a standard workout
//want to pass a diff workout if a diff button pressed
#IBAction func standardWorkoutPressed(_ sender: UIButton) {
performSegue(withIdentifier: "goToWorkout", sender: self )
}
My problem after playing around with it for a few hours is how to elegantly pass a different workout to the workout page.
i.e. I guess I could literally just copy and paste all the code for each button and make a new segue for each but that seems like the wrong way to do it!
The sort of thing I've tried is defining the workouts as variables in an if else if section but then the final workouts are out of scope for the segue.
Hope this makes sense, answers I can find about conditional segues seem to mostly refer to 'only allow the segue to happen under this condition' rather than pass different data sets to the same destination. e.g. example1 and example2
I'll add my comment as an answer instead, to make it easier to show some code examples.
Add a property to your viewcontroller:
var selectedWorkout : FinalWorkout!
in each of your three button action methods you set this property to the workout associated with each button. So for your standard workout:
#IBAction func standardWorkoutPressed(_ sender: UIButton) {
let finalTimeForWorkout = Int(timeInputField.text!)
self.selectedWorkout = FinalWorkout(generatedWorkout: WorkoutGenerator.standardWorkout.generate(), timeForWorkout: finalTimeForWorkout!)
performSegue(withIdentifier: "goToWorkout", sender: self )
}
Finally:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
let finalWorkoutTime = selectedWorkout.timeForWorkout
let finalWorkoutExercises = selectedWorkout.generatedWorkout.workoutExercises
if let destVC = segue.destination as? WorkoutController {
destVC.selectedWorkoutExerciseArray = finalWorkoutExercises
destVC.selectedWorkoutTime = finalWorkoutTime
}
}
}
In performSegue(withIdentifier:sender:), sender can be anything you like.
Use sender to pass the generated workout to performSegue:
//use this button to pass a standard workout
//want to pass a diff workout if a diff button pressed
#IBAction func standardWorkoutPressed(_ sender: UIButton) {
performSegue(withIdentifier: "goToWorkout", sender: WorkoutGenerator.standardWorkout.generate() )
}
Then in prepare(for:sender:)
let finalWorkout = FinalWorkout(generatedWorkout: sender as! YourWorkoutGeneratorType, timeForWorkout: finalTimeForWorkout!)
If I have this right, you want to pass a different argument into your FinalWorkout() function based on which button was pressed?
It doesn't always make for very readable code, but you can use the UIButton tag property. If you set each button's tag property to a unique value [0, 1, 2], you can use that info to generate a different workout:
// This code would be in: override func prepare(for segue: UIStoryboardSegue, sender: Any?)
let buttonTag = (sender as! UIButton).tag
if buttonTag == 0 { // Generate type of workout}
else if buttonTag == 1 { // Generate type of workout }
else if buttonTag == 2 { // Generate type of workout }
Or if you're worried about tags not being an intuitive representation, you could just test for equality with the IBOutlet reference you have for each button:
let buttonPressed = sender as! UIButton
if (buttonPressed == self.yourButtonIBOutletPropertyName) { // select a workout for this button}
I may have missed what you were asking, if this is the case comment and I'll update the answer.

Updating TableView within containerview

I have my UIViewController, which is a person's profile page, it displays their name and picture. Within that UIViewController I have a UIContainerView that displays a static UITableView. I'm having a little trouble updating the cells of the table when the profile page loads. Here's my storyboard:
I have tried the following, within my UIViewController.
I created an outlet to my UIView and passed the person object to it.
In my UIView I called the segue to pass the object to the UITableViewController and from there I was going to update some labels.
func prepareForSegue(segue: UIStoryboardSegue!, sender: Any?){
if segue.identifier == "containerSegue"{
let statsTable = segue.destination as! CollectionTableViewController
statsTable.currentPerson = currentPerson
}
}
My Segue is never called though. I have moved it to the main UIViewController in case it should be called from there, but again not called. What am I doing wrong with this approach?
Assuming you have set the Segue Identifier to "containerSegue" you should get it in your "Stats" view controller.
Are you using Swift 3? It's possible you just have the func definition wrong:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "containerSegue" {
let statsTable = segue.destination as! CollectionTableViewController
statsTable.currentPerson = currentPerson
}
}
That should work.

How do I get the property value from a container view in Swift

I have 1 UIViewController that contains a UIContainerView and a UIButton.
Also, I have a UITableViewController (It has a UITextField and a UITextView) that is embedded in the UIContainerView in the UIViewController.
I'm trying to get the string values that will be available in TextField and in TextView.
I tried to use the segue to get the properties values, but I failed to do so, see the code below.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TVCSegue" {
let containerTVC = segue.destination as? FieldsTVC
self.textField.text = FieldsTVC?.textField?.text
self.textView.text = FieldsTVC?.textView?.text
}
}
The code above has 'textField' and 'textView' as properties in the UIViewController to assign the values to.
But I believe that it doesn't work as I get the values before they changes. Please provide me with a functional way to do so.
When your main view loads, the container performs a segue to its assigned initial / root ViewController. At that point, you can get a reference to it:
var theFieldsTVC: FieldsTVC?
Now, in prepare for segue, assign that variable:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TVCSegue" {
if let vc = segue.destination as? FieldsTVC {
theFieldsTVC = vc
}
}
}
Then, you can:
#IBAction func buttonTapped(_ sender: Any) {
self.textField.text = theFieldsTVC?.textField?.text
self.textView.text = theFieldsTVC?.textView?.text
}

Resources