dismissing a view never calls completion function - ios

When I dismiss a view it refuses to trigger the completion function and I don't know why.
Here I expect the print statement to execute but it never does.
#IBAction func tapBack(_ sender: Any) {
self.navigationController?.popViewController(animated: true)
self.dismiss(animated: true, completion: {
print("this should print")
})
}
Source Code
https://github.com/omenking/DismissCompletion
I also tried wrapping it in DispatchQueue.main.async but I had no luck.

Would you not be better off calling the function on the tap? Example:
func dismissController() {
self.navigationController?.popViewController(animated: true)
self.dismiss(animated: true, completion: nil)
}
And then on your button:
#IBAction func tapBack(_ sender: Any) {
dismissController()
print("this should print")
}
I've just downloaded your project and tried this and it prints.

First of all , I am assuming that since you are popping the view controller, you must have pushed it into the navigation Stack.
Secondly, if you did do it , then self.dismiss method will never get called because you are already popping the view controller before it.
If you want the completion block to work, you should present the ViewController instead of pushing it.Then, you can write your code in the completion block and it will execute.

Related

Swift dismissing view with clunky animation

I simply have a ViewController that I would like to dismiss. And this is my dismissAction:
#objc private func dismissView(){
self.dismiss(animated: true, completion: nil)
UserDefaultsService.shared.updateDataSourceArrayWithWishlist(wishlist: self.wishList)
let dataSourceArray = UserDefaultsService.shared.getDataSourceArray()
// update datasource array in MainVC
self.dismissWishlistDelegate?.dismissWishlistVC(dataArray: dataSourceArray, dropDownArray: self.dropOptions, shouldDeleteWithAnimation: false, wishlistToDelete: self.wishList)
}
Proble:
The dismiss animation is very clunky and not fluent at all. I found out that if I remove everything in the function but only call self.dismiss it is working perfectly fine. What is the issue here? Any idea on how I can fix this?
You can try to light-weight load in main thread by
DispatchQueue.global().async {
UserDefaultsService.shared.updateDataSourceArrayWithWishlist(wishlist: self.wishList)
}
And instead of let dataSourceArray = UserDefaultsService.shared.getDataSourceArray() use self.wishList directly in the last line

Where to use popToRootViewController to pop extra VC off stack?

I have a view controller with two buttons:
Button1 brings up the documentPickerViewController and lets user pick a file.
Button2 goes to a second view controller.
Button1 is linked to "openFile" in the code below.
Button2 calls the segue to the second view controller.
So this is how I get the problem:
Click Button1, document picker shows up.
If I pick a document, then document picker disappears and I'm back to view controller.
So far so good.
Now I press Button2. Second view controller shows up. All good. I exit and go back to 1st view controller.
Now I press Button1 again. Document picker shows up.
But this time I click "cancel". Document picker disappears but the SECOND view controller appears!
I get
"Unbalanced calls to begin/end appearance transitions for <_UIWaitingForRemoteViewContainerViewController: 0x122206480>." and
"[DocumentManager] The view service did terminate with error: Error Domain=_UIViewServiceErrorDomain Code=1 "(null)" UserInfo={Terminated=disconnect method}"
From researching I understand that I must have popped an extra 2nd view controller to the stack but I can't see where I would have done it and also where would be the appropriate place to pop it?
I've tried setting "animated: false" and that didn't make any difference.
Thanks in advance.
#IBAction func openFile(_ sender: Any) {
let documentPicker: UIDocumentPickerViewController = UIDocumentPickerViewController(documentTypes: ["public.text"], in: UIDocumentPickerMode.import)
documentPicker.delegate = self
documentPicker.modalPresentationStyle = UIModalPresentationStyle.fullScreen
self.present(documentPicker, animated: true, completion: nil)
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
self.dismiss(animated: true, completion: nil)
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
if controller.documentPickerMode == UIDocumentPickerMode.import {
var textRead = ""
do {
if urls.count > 0 {
for i in 0...urls.count-1 {
textRead = try String(contentsOf: urls[i], encoding: .utf8)
textView.text = textView.text + textRead
}
}
}
catch {
/* error handling here */
print("There's a problem reading the file")
}
}
}
I'm not sure what you want to do in your implementation of documentPickerWasCancelled(_:) but know that if you just want to hide the document picker, you don't need to explicitely call self.dismiss(animated: true, completion: nil): when you tap 'Cancel' the document picker dismisses itself already.
After more experimenting and researching, I found this stopped the 2nd view controller from appearing, even though the two error messages are still appearing:
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
//self.dismiss(animated: true, completion: nil)
self.navigationController?.popToRootViewController(animated: true)
}
Would still appreciate if someone can suggest a more elegant solution which eliminates the error messages altogether.
EDIT:
After playing with it a bit more, I found the fix!
We only need this:
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
// do nothing. The picker is dismissed anyway.
}
So the dismiss statement was causing the problem. By leaving out any dismiss picker call, the picker window closes anyway.

SWIFT 4 Present viewcontroller

I am trying to building a app using the master details template.
in the Master view controller I added a button called Catalogue : this button showing a tabbar controller called Catalogue.
I don't use Segue to show the catalogue, I use the code below to show the tab controller
From Master form I called the Tabbar controller :
#IBAction func Btn_Catalogue(_ sender: Any) {
let AddCatalogueVC = self.storyboard?.instantiateViewController(withIdentifier: "CatalogueVC") as! CatalogueVC
present(AddCatalogueVC, animated: true, completion: nil)
}
From CategorieVC I use the code below to show
#IBAction func Btn_AddCategorie(_ sender: Any) {
self.Mode = "New"
let AddCategorieViewController = self.storyboard?.instantiateViewController(withIdentifier: "AddCategorieVC") as! AddCategorieVC
present(AddCategorieViewController, animated: true, completion: nil)
}
I dismiss the AddCategorieVC using the code below
#IBAction func Btn_Save(_ sender: Any)
{
if self.Txt_CategorieName.text != ""{
self.Mysave = true
self.MyCategorieName = self.Txt_CategorieName.text!
self.dismiss(animated: true, completion: nil)
}
}
I have unwind SEGUE from Save button to a function in categorieVC
#IBAction func FctSaveCategories(_ sender: UIStoryboardSegue) {
let sendervc = sender.source as! AddCategorieVC
if self.Mode == "New" && sendervc.Mysave == true { // Mode insert
let MyCategories = TblCategorie(context: Mycontext)
MyCategories.categorie_Name = sendervc.MyCategorieName
do {
try Mycontext.save()
} catch {
debugPrint ("there is an error \(error.localizedDescription)")
}
}
}
The problem is when I hit the save button in categorieVC the catalogueVC is also dismissing at the same time returning me to the master control.
I am almost sure that the problem came from the Unwind segue but I don't know why.
Update: I activate the Cancel button in AddCategorieVC with the code below
self.dismiss(animated: true, completion: nil)
and when I clicked on it only the AddCategorieVC is being dismissed and I go back to CatalogueVC. The difference between the save button and the Cancel Button is only the UNWIND segue on the Save Button.
And when I add UnWIND segue to the cancel Button (just to test the behavior) it took me back to the master form instead CatalogueVC.
How can I solve that?
And yesss I found it
It look like that unwind segue automaticly handled dismiss contrĂ´le
So all I need to do is remove the dismiss code from the save button this way the unwind segue will took me back to catalogueVC.
.

Dismiss pushed view controller

So I have a view controller which I display as follows:
func showProfileForTrainer(trainer: Trainers) {
let viewPTProfileVC = ViewPTProfileVC()
viewPTProfileVC.trainer = trainer
self.navigationController?.pushViewController(viewPTProfileVC, animated: true)
}
But when trying to dismiss the view, I cannot get it to work. It has a back button in a navigation bar which functions fine, but when trying to dismiss the view via a button for example, it does nothing. I have currently tried:
func handleMessageTrainer() {
dismiss(animated: true) {
print(1)
self.tabBarVC?.showChatLogForTrainer(trainer: self.trainer!)
}
self.dismiss(animated: true) {
print(2)
self.tabBarVC?.showChatLogForTrainer(trainer: self.trainer!)
}
navigationController?.dismiss(animated: true, completion: {
print(3)
self.tabBarVC?.showChatLogForTrainer(trainer: self.trainer!)
})
self.navigationController?.dismiss(animated: true, completion: {
print(4)
self.tabBarVC?.showChatLogForTrainer(trainer: self.trainer!)
})
print(5)
}
As you can see I have tried varying ways and none work, and the console just outputs 5.
Frustratingly, elsewhere in my app I presented a view in the same way as shown at the beginning and it dismissed fine using dismiss(animated: true) { ... }
Any idea why it won't work here?
You must pop the view controller from the corresponding navigation controller:
self.navigationController?.popViewController(animated: true)
If you are using pushviewcontroller method then to dismiss you have to use popviewcontroller method.
Try this:
self.navigationController?.popViewController(animated: true)

swift: How to have same closure to be executed from two separate methods

I have a situation where I have function say
func openViewController(completion:(success:Bool) -> Void)
{
//code here to present some view controller name MYVC
self.presentViewController(myVC, animated: true, completion: {
})
}
From MYVC I get call back with help of delegate in below function in same class as I called above method of openViewController
func handleDismissOfVC(){
self.dismissViewControllerAnimated(true, completion:{
})
}
Now the challenge for me is to call completion block of openViewController of with success flag when I dismiss the view controller with handleDimissOfVC(). How can I achieve this?
You need to create an instance variable to hold the closure. in openViewController, save the closure to that instance variable.
In your handleDismissOfVC function, invoke the block that was saved to the instance variable.
You'd need to provide the completion block to MyVC as a property, like this:
class MyVC {
// Completion callback for when this VC is dismissed.
var complete: ((success: Bool) -> Void)?
func dismiss() {
self.dismissViewControllerAnimated(true, completion:{
if let c = complete {
c(success: true)
}
})
}
}
func openViewController(completion:(success:Bool) -> Void) {
// Pass the completion block to the VC.
myVC.complete = completion
self.presentViewController(myVC, animated: true, completion: {
})
}
I think something like that should do it. Although, this looks like it may be a better fit for the delegate pattern, depending on where the original block is originating from.

Resources