When dismiss child parent VC reloads completely - ios

I have so strange issue. I have parent VC named NewAdCreationViewController it presents modally child VC named CollectionPicturesViewController.
In child VC I have link to the parent VC.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == picturesCollectionVCSegueName {
let collectionController = (segue.destination as! UINavigationController).viewControllers.first as! CollectionPicturesViewController
let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? PicturesCollectionTableViewCell
guard cell != nil else{ return }
collectionController.picturesArray = cell!.itemPictures
collectionController.parentVC = self // this is link!
}
}
In child VC I present another VC for picking photos - DKImagePickerController(pod). After picking all my photos appears in collectionView in child VC, and when I tap "Save" I want to pass all that data to parent VC and dismiss child,
#objc func savePictures(){
self.parentVC.pictures = self.picturesArray
self.parentVC.tableView.reloadData()
self.dismiss(animated: true, completion: nil)
}
but after I dismiss it my parent reloads completely and starts from viewDidLoad. It presents completely new VC(I've checked in console, it has another address in memory). I really don't know why is that?

On highlighted segue I've changed presentation to Over full screen. And it works perfectly without reloads!

Related

Calling function with argument from segue

I have a FirstVC with table view with segue to another SecondVC where i pass the name of pressed cell. In SecondVC i have another tableview which i want to populate with data. I have a function called gettingUrls() which is pulling data for my table view:
DataService.instance.mainRef.child("Folders").child("\(folderName)").observeSingleEvent(of: .value) { (snapshot: DataSnapshot) in...}
where "folderName" is the value from FirstVC. This function is implemented into "pull to refresh feature" and it works great. However when i try to run the function itself in viewDidLoad it keeps crashing the app because of empty folderName value. I just can't understand why at this point the value from segue is available in refresh function and not in viewDidLoad.
Here is the code of segue:
let detailVC = self.storyboard?.instantiateViewController(withIdentifier: "DetailVC") as! DetailVC
self.navigationController?.pushViewController(detailVC, animated: true)
self.present(detailVC, animated: true, completion: nil)
detailVC.folderName = fileName
How can i run the function in viewDidLoad?
You're presenting the DetailVC's view before you've set its folderName property. The view has loaded but it doesn't yet have a folderName so the program crashes. You should place the line detailVC.folderName = fileName before you present the DetailVC.
You also don't need both the 2nd and 3rd lines - either present the new view controller or push it onto the navigation stack, not both.
You should write code with segues to push viewControllers like following not with navigationController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "Verification") {
let vc = segue.destination as! MyViewController
vc.delegate = self
vc.property = value
}
}
So with #haarismuneer help, it was enough to pass the variable before initializing SecondVC:
detailVC.folderName = fileName
self.navigationController?.pushViewController(detailVC, animated: true)

Performing segue after sender view controller is dismissed

Basically, I have a button in a slide-out menu (which is its own view controller that covers part of the Origin screen, let's call it Menu) that, when pressed, performs a modal segue to another controller, let's say Destination.
Is there any way that upon pressing the button in Menu (to go to Destination), that I can dismiss Menu back to Origin, and THEN segue to Destination?
It sounds silly but it's something that I think I've seen apps do before. In my case, the reason for wanting to do this is that once I press "Done" on Destination, it dismisses that controller back to Menu, when I want it to just dismiss back to Origin. I can't just perform a segue back to Origin from Destination.
Code:
This is how I open the Menu from Origin:
let interactor = Interactor()
#IBAction func openMenu(_ sender: AnyObject) {
performSegue(withIdentifier: "openMenu", sender: nil)
}
#IBAction func edgePanGesture(sender: UIScreenEdgePanGestureRecognizer) {
let translation = sender.translation(in: view)
let progress = MenuHelper.calculateProgress(translationInView: translation, viewBounds: view.bounds, direction: .Right)
MenuHelper.mapGestureStateToInteractor(
gestureState: sender.state,
progress: progress,
interactor: interactor){
self.performSegue(withIdentifier: "openMenu", sender: nil)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? MenuViewController {
destinationViewController.transitioningDelegate = self
destinationViewController.interactor = interactor
destinationViewController.currentRoomID = self.currentRoomID
}
}
This is my prepareForSegue from Menu to Destination currently, nothing fancy:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
let inviteVC = segue.destination as! InviteVipViewController
inviteVC.currentRoomID = self.currentRoomID
}
And finally to dismiss Destination is just a simple
#IBAction func cancelButtonPressed(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
I saw this question which is basically what I'm trying to do but there was no answer unfortunately: Performing segue after dismissing modal swift
Sorry if this sounds confusing, but if anyone knows what I'm talking about and can let me know how I can set up the segues/prepareForSegues to make it work, any input would be appreciated!
Based on a modification to this answer, the following should work:
In your storyboard, remove the segue that is triggered by tapping your menu button and goes to Destination.
Create a new segue that goes from the Origin view controller to Destination view controller. This segue is going to be manually performed.
When your Destination option is selected in Menu, have Menu dismiss itself and then perform the Destination segue on Origin, like this:
// This code goes in Menu, and you should call it when
// the menu button is tapped.
//
// presentingViewController is Origin
weak var pvc = self.presentingViewController
self.dismiss(animated: true) {
// Menu has been dismissed, but before it is destroyed
// it calls performSegue on Origin
pvc?.performSegue(withIdentifier: "openDestination", sender: nil)
}
When Destination is dismissed, you should see Origin, without seeing Menu at all.
I tested this in a sample app where "Menu" was not a slide out, but a full modal view controller, and it worked for me.
EDIT: While troubleshooting with #KingTim, he found that we needed to wire the segue from the UINavigationController, not Origin, to the Destination. This is because Origin is inside a navigation controller. After that discovery, it worked.
If your presenting view is embedded in a navigation controller then you can do this:
weak var pvc:UIViewController! = self.presentingViewController?.childViewControllers[0]
dismiss(animated: true)
{
pvc.performSegue(withIdentifier: "SegueID", sender: nil)
}
Simple solution with presentingViewController
if let destinationVC = self.presentingViewController as? YourViewController {
destinationVC.isBooleanPassed = true
destinationVC.selectedString = "here comes your string"
destinationVC.selectedInteger = 12345
}
dismiss(animated: true, completion: nil)

Cannot update embedded child VC from non-parent VC

Up to date Xcode/Swift/iOS.
I have a Master VC (called StartVC) that contains a Child VC (called TopBarVC) via and embedded segue. The Child VC contains a button, that, when pressed, modally segues to a 3rd VC (called CategoryPickerOverlayVC) (the view in this VC serves as a dropdown box for picking a category).
#IBAction func CategoryFilterButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "toCategoryPickerOverlay", sender: self)
}
When an option is selected from the dropdown box, which itself is composed of three buttons, the title of the selected button should be used to replace the title text of the button in the Child VC.
In the Master VC, I use prepareforsegue to store a reference to the Child VC in a variable - "topBarReference" - at the moment when the embed segue takes place.
var topBarReference: TopBarVC?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TopBarPane"{
topBarReference = segue.destination as? TopBarVC
}
}
Then, in the 3rd VC, when I click on one of the button options in the dropdown box, the button title is sent via a prepareforsegue to update the button in the Child VC (via "topBarReference").
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.filterButtonText = ((sender as! UIButton).titleLabel?.text)!
}
The 3rd VC then unwind segues back to the Master VC. I should add that when the button in the Child VC is changed, a variable (filterButtonText) in Child VC is first set with the title text and then this variable is then used to set the button title text via the viewDidAppear method of Child VC.
When using the debugger, I also note that viewDidAppear in the Master VC does not seem to execute after unwinding (I placed a diagnostic print-to-console in viewDidAppear and nothing prints after the unwind segue). I realise this would explain the button not getting updated but I've got no idea why viewDidAppear does not run.
I have also tried using a delegate protocol and instantiateViewController(withString:) to no avail. All of the methods produce the same result, which is that the button in the Child VC does not get updated. No errors are shown. Everything else happens as expected.
Any ideas as to what I am doing wrong?
Do you mean something like this?
If so, the solution I used was very simple: the third VC uses prepareForSegue to set a property of the embedded VC, and the embedded VC picks up that property in the unwind method.
In my implementation, the three view controllers are called ViewController, ChildViewController, and ThirdViewController. This is the entire code (everything else is configured in the storyboard):
class ChildViewController: UIViewController {
#IBOutlet weak var theButton: UIButton!
var buttonTitle : String?
#IBAction func unwind(_:UIStoryboardSegue) {
self.theButton.setTitle(self.buttonTitle, for: .normal)
}
}
class ThirdViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
(segue.destination as! ChildViewController).buttonTitle = (sender as! UIButton).currentTitle
}
}
Ok, so I have found that my original code works fine bar one line in the prepareforsegue of the Child VC. If I change that prepareforsegue from:
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.CategoryFilterButton.titleLabel?.text = ((sender as! UIButton).titleLabel?.text)!
}
to this:
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.CategoryFilterButton.setTitle((sender as! UIButton).titleLabel?.text, for: .normal)
}
it works just fine. The use of the .setTitle method seems to make a difference although I am not sure why.
Thanks to Matt for giving me the idea to change it to that. Matt's method did work when i tried it, although, as I am unwinding to the Master VC and not the Child VC, I had to edit the code accordingly, in terms of where I placed it.
As my little "discovery" equates to the smallest change to the original code, I'll mark this as the answer.
Thanks to all for taking the time to respond!

Very poor performance after dismissViewController

I have a view controller that presents another one modally which contains a gif and a table view with animation on the cells, and when I dismiss it, the app has an annoyingly long delay before you can do anything again (like 3-5 seconds).
I've found answers that say it's because I still have a reference to the vc after its dismissal, but I don't see how that's possible because the only place I make a reference to it is in prepareForSegue.
Any suggestions?
Edit 1:
Here is my prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destVC = segue.destinationViewController as! InWorkoutViewController
destVC.workout = workout
}
The destVC's workout property is an optional custom class.
Edit 2:
Here is how the VC is dismissed (still very slow):
#IBAction func tappedX(sender: AnyObject) {
dispatch_async(dispatch_get_main_queue(), { [unowned self] in
self.dismissViewControllerAnimated(true, completion: nil)
})
}

UISplitViewController return from master to detail

Is it possible on iPhone via UISplitViewController to return from the master controller to the detail one? I have tried in master changing displayMode to PrimaryHidden, using showDetailViewController with splitViewController?.viewControllers[0], but nothing was able to show back the detail controller.
I would like to have both controllers without dismissing and reloading, because one includes a map and the second one preferences of map objects.
Lets say you have a button in the master view which when pressed will take you to detail view. Here is an example how that would go
#IBAction func buttonToGoToDetailView(sender: UIButton) {
performSegueWithIdentifier("segue id in storyboard", sender: nil)
}
// prpare for segua to detail view
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destination = segue.destinationViewController as? UIViewController
if let dvc = destination as? NameOfTheDetailViewController{
if segue.identifier = "segue id in storyboard" {
//use dvc to pass data to the detail if there is any
}
}
}

Resources