Very poor performance after dismissViewController - ios

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)
})
}

Related

reset variables when pass to an another ViewController

my problem is that when i switch to an another ViewController, my variables of the previous VC call are reset so i can't do what i want after
#IBAction func BackBtn(_ sender: Any) {
self.nbrQst = 10
self.Switch1A = 2
performSegue(withIdentifier: "Numero", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Numero" {
let vc = segue.destination as! ViewController
vc.nbrQt = nbrQst
}
if segue.identifier == "Numero" {
let vcNv = segue.destination as! ViewController
vcNv.Switch1 = Switch1A
}
}
below the way that i send information from my Lvl1 file to the lvl selector file to add a if else for unblocks the lvl
#IBAction func BackBtn(_ sender: Any) {
self.nbrQst = 10
self.Switch1A = 2
performSegue(withIdentifier: "Numero", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ViewController
vc.nbrQt = nbrQst
vc.Switch1 += 2
}
that's the second ViewController who send the number to the first one, and i want that the first one remember the number and add them up each time I press the button that sends the numbers
Let's call "Selector VC" the main VC.
The reason why those numbers reset in main VC is that when you press the "BackBtn", you're not actually going back to main VC, you're creating a new main VC and going there (because you did performSegue(withIdentifier: "Numero", sender: self) in BackBtn function, performSegue always takes you to a new VC rather than taking you back). The new main VC has all those fields starting from an initial number (I assume initially they're zero).
What you want to do is to go BACK, not forward. Depending on how you went to the level VC from main VC, you have different ways of going back to main VC.
If you're pushing everything onto a navigation controller, then you can go back by:
self.navigationController?.popToRootViewController(animated: true)
If you're presenting everything modally, you can go back by
self.dismiss(animated: true, completion: nil)
You said, you want to update the value in main VC so that you can block/unblock next level. A very standard way to achieve this is via delegate, please Google this ("delegates in Swift") and follow their tutorials there.
I strongly recommend that you take an online course on swift development before you go ahead and develop your own app, because you could potentially do a lot of things wrong and waste a lot of your time trying to get an answer from Stack Overflow.

When dismiss child parent VC reloads completely

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!

navigation bar disappear after adding prepare function

I am woking on an recording APP.
I tried to add navigation controller in my first recording viewcontroller which then could pass filename array to the second viewcontroller using the following function prepare:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let controller = segue.destination as? MainViewController
controller?.recordArray = recordingArray
self.present(controller!, animated: true, completion: nil)
}
However, when ran in the simulator the navigation bar disappeared in the second controller and Xcode pops out the warning
Thread1: EXC_BAD_ACCESS(code=2,address=0x7fff51edfff8)
Has anyone got any advice?
Thanks!
Don't ever call present(_:animated:completion:) inside prepare(for segue:).
prepare(for segue) is called automatically by the system just before a segue is about to happen to let you prepare the data for sending to the destination viewcontroller or do any other calculations you need to before performing the segue. A segue needs to be set up in Storyboard and it will either be called automatically or if it is a manual segue, you need to call it using perform(segue) and once you do that, the system will call prepare(for segue) for you. You can see why calling another navigation function proves to be problematic, since you are trying to navigate to another viewcontroller using two different methods (segue and present).
If you haven't set up the segue in Storyboard, then you also need to do that, since if it is not set up, let controller = segue.destination as? MainViewController will be nil.
Once you set up the segue in Storyboard, this is how your function should look like:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? MainViewController {
controller.recordArray = recordingArray
self.present(controller, animated: true, completion: nil)
}
}
You shouldn’t be trying to present a VC in this method, it’s just a place for you to configure the destination VC before the segue presents it
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let controller = segue.destination as? MainViewController else { return }
controller.recordArray = recordingArray
}

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)

is there a way of dismissing two uiViewControllers at the same time in Swift?

In my swift app I have a UIViewController with a button. This button opens up the UIViewController number 2 and there user has another button. When user presses it - he opens UIViewController number 3. There is also a button and when user presses it - he calls the code:
self.dismissViewControllerAnimated(false, completion: nil)
and thanks to it the UIViewController number 3 disappears and user sees UIViewController number 2. My question is - is there a way of also dismissing the UIViewController number 2 so that user can come back smoothly from number 3 to number 1?
For now I created a function and call it through protocol:
UIViewController number 2:
protocol HandleYourFullRequest: class {
func hideContainer()
}
class FullRequest: UIViewController, HandleYourFullRequest{
func hideContainer(){
self.dismissViewControllerAnimated(false, completion: nil)
}
#IBAction func exitbuttonaction(sender: AnyObject) {
self.performSegueWithIdentifier("temporarySegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "temporarySegue"){
if let fullRequestDetails = segue.destinationViewController as? YourFullRequest
{
fullRequestDetails.delegateRequest = self
}
}
}
}
UIViewController number 3:
class YourFullRequest: UIViewController{
var delegateRequest:HandleYourFullRequest?
#IBAction func exitbuttonaction(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
delegateRequest?.hideContainer()
}
}
But with that solution when user presses the button - the UIViewController number 3 disappears and UIViewController number 2 appears for a second and disappears then. Is there a way of removing number 2 without showing it to the user and point him directly to the number 1?
I'm still unclear as two which button is wired to which action, but from what I can tell when the dismiss button is pressed on view controller 3 it calls self.dismissViewControllerAnimated(false, completion: nil) in view controller number 2.
Try putting this method in view controller 3.
#IBAction func exitButtonAction(sender: AnyObject) {
self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil);
}
This will work assuming that both view controllers are presented and not pushed in something like a navigation controller.
You can use removeLast() function to pop controllers off the stack.
#IBAction func doneAction(sender: AnyObject) {
var vc = self.navigationController?.viewControllers
// remove controllers from the stack
vc?.removeLast()
vc?.removeLast()
// Jump back to the controller you want.
self.navigationController?.setViewControllers(vc!, animated: false)
}
You can use popToViewController(viewController: UIViewController, animated: Bool)
Where viewController is the viewController you wish to pop to, in your case, 'UIViewController number 1'.
popToViewController Documentation
If you don't have a reference to the View Controller, you can get it from self.navigationController.viewControllers, it will be the first object in your example.
There is a method on UINavigationController called setViewControllers. It takes an array of all the view controllers you want active. You can get all the view controllers on the stack as an array, remove the ones you don't want, then call setViewControllers with the updated array.

Resources