is it possible to handle and trigger some func in first controller if second controller is dimissed?
this my code to open second controller from first controller
self.present(UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LockScreen") as UIViewController, animated: true, completion: nil)
and this my code to dismiss controller from second controller to first controller
self.navigationController?.popViewController(animated: true)
self.dismiss(animated: true, completion: nil)
Inside your LockScreen controller, declare a closure to handle when dismissed:
class LockScreen: UIViewController {
var onDismissHandler: (() -> ())?
func dismissController() {
self.dismiss(animated: true, completion: onDismissHandler)
}
}
Then when you're presenting LockScreen:
func onPresent() {
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "LockScreen") as LockScreen
controller.onDismissHandler = { [weak self] in
// TODO: Do something when dismissed
}
self.present(controller, animated: true, completion: nil)
}
When second viewController is dismissed, trigger some func on dismissing of the current viewController.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction private func buttonClick(sender: UIButton) {
let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController")
onDismiss()
self.present(viewController, animated: false, completion: nil)
}
Trigger some func on dismissing of the current view controller
func onDismiss() {
self.dismiss(animated: false) {
print("Dismissing current view controller")
}
}
}
Related
I have two Present view controllers. The thing i want to do is when the second Present view controller is dismissed it will automatically reload the first present view controller(Table view). note: first view controller holds a table view, basically i want to reload the table view of first controller.
ViewWillAppear code:
override func viewWillAppear(_ animated: Bool) {
tableViewReloadFromCreateProductVC()
}
func tableViewReloadFromCreateProductVC () {
tableView.reloadData()
}
Calling from second view controller code:
SecondViewController.tableViewReloadFromCreateProductVC()
navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
FirstViewController calling 2nd view controller
#IBAction func CallSecondViewButton(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "YourViewControllerIdentifier") as! YourViewController
controller.modalPresentationStyle = .fullScreen
self.present(controller, animated: true, completion: nil)
}
just write the code in viewWillAppear() method of the view controller that you want to reload like this
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
//perform api call if any
yourTableView.reloadData()
}
2nd view controller
#IBAction func CloseButton(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
after dissmissing the viewWillAppear method of firstViewController will autometically called.
The First two snippets are for first view controller and the last one is for second view controller
Reloading the entire table view could sometimes be costly and it also sounds like you're making an API call as well so unless you want your table view to be reloaded and the API call made every time the view controller becomes visible whether or not you've made changes to it, you want the reloading to be done only when it's necessary.
You can try it in a few different ways:
class CreateProductVC: UITableViewController {
#IBAction func presentSecondVC() {
if let secondVC = storyboard?.instantiateViewController(identifier: "SecondVC") as? SecondViewController {
secondVC.delegate = self
present(secondVC, animated: true, completion: nil)
}
}
}
class SecondViewController: UIViewController {
weak var delegate: CreateProductVC?
#IBAction func dismissSecondVC() {
self.dismiss(animated: true) {
self.delegate?.tableView.reloadData()
}
}
}
or
class CreateProductVC: UITableViewController {
#IBAction func presentSecondVC() {
if let secondVC = storyboard?.instantiateViewController(identifier: "SecondVC") as? SecondViewController {
secondVC.isDismissed = { [weak self] in
self?.tableView.reloadData()
}
present(secondVC, animated: true, completion: nil)
}
}
}
class SecondViewController: UIViewController {
var isDismissed: (() -> Void)?
#IBAction func dismissSecondVC() {
self.dismiss(animated: true) {
self.isDismissed?()
}
}
}
or if you want more fine-grained control over what to do with the new data:
protocol ReloadVC {
func reload(_ value: String)
}
class CreateProductVC: UITableViewController, ReloadVC {
var dataSource: [String]! {
didSet {
tableView.reloadData()
}
}
#IBAction func presentSecondVC() {
if let secondVC = storyboard?.instantiateViewController(identifier: "SecondVC") as? SecondViewController {
secondVC.delegate = self
present(secondVC, animated: true, completion: nil)
}
}
func reload(_ value: String) {
dataSource.append(value)
}
}
class SecondViewController: UIViewController {
var delegate: ReloadVC?
#IBAction func dismissSecondVC() {
self.dismiss(animated: true) {
let someValue = "Some Value"
self.delegate?.reload(someValue)
}
}
}
I have two view controller
imag
1.first view controller is main and second is CustomAlertviw controller
main view controller is like above image, when I click continues am showing the customalertview controller, like image. when click the enter pin button I have to dismiss view contoller and show the view controller with uiview on main view controller but tried, it's showing the view controller, there one more custom view is not showing it crashing my app
main view code
#IBAction func backButtonClicked(_ sender: UIButton) {
let myAlert = UIStoryboard(name: "CustomAlertview", bundle: nil).instantiateViewController(withIdentifier: "CustomAlertViewController") as? CustomAlertViewController
myAlert?.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
myAlert?.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(myAlert!, animated: true, completion: nil)
}
Customalertview controller
#IBAction func enterPinButtonClick(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
let myAlert = UIStoryboard(name: "ScanAndPay", bundle: nil).instantiateViewController(withIdentifier: "ScanAndPayDetailsEntryViewController") as? ScanAndPayDetailsEntryViewController
myAlert?.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
myAlert?.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(myAlert!, animated: true, completion: nil)
myAlert.valuespass()
}
inside main view controller one function calling from the customalrerview
func valuespass()
{
DispatchQueue.main.async {
self.authType = 1
self.authStatus = false
print("this is calling")
self.pinStatusLabel.text = "Please enter a 4-digit Security PIN"
}
when I am calling this function from the custom alert view application is crashing and showing the hread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value this error. how to can show the all information after dismissing the custom view controller.
You are almost done. present ScanAndPay from FirstView instead of CustomAlertview.
I have done using Delegate as I think it will be fit in this scenario. Follow below steps -
Step 1:
In FirstViewController ,
Add - CustomAlertviewDelegate something like this -
class FirstViewController: UIViewController, CustomAlertviewDelegate {
Now replace your backButtonClicked method -
#IBAction func backButtonClicked(_ sender: UIButton) {
let myAlert = UIStoryboard(name: "CustomAlertview", bundle: nil).instantiateViewController(withIdentifier: "CustomAlertViewController") as? CustomAlertViewController
myAlert?.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
myAlert?.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
myAlert.delegate = self
self.present(myAlert!, animated: true, completion: nil)
}
Add a new Delegate method of CustomAlertviewDelegate -
func ScanAndPayView(){
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let myAlert = UIStoryboard(name: "ScanAndPay", bundle: nil).instantiateViewController(withIdentifier: "ScanAndPayDetailsEntryViewController") as? ScanAndPayDetailsEntryViewController
myAlert?.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
myAlert?.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(myAlert!, animated: true, completion: nil)
}
}
Step 2:
Now time for CustomAlertview ,
Add protocol for delegate top of the Class -
protocol CustomAlertviewDelegate: class {
func ScanAndPayView()
}
class CustomAlertview: UIViewController{
weak var delegate : CustomAlertviewDelegate!
...
override func viewDidLoad() { // Rest of your code
}
Now time to dismiss current view controller and notify FirstViewController for presenting ScanAndPay view Controller -
#IBAction func enterPinButtonClick(_ sender: Any) {
delegate.ScanAndPayView()
self.dismiss(animated: true, completion: nil)
}
Hope it will help you to achieve what you want.
I am using dispatcQueue to automatically dismiss my controller but I have an issue is that it dismiss my view controller automatically but also dismiss other view controllers that are presented modally and I want to dismiss only a single view controller
func dismissViewController() {
let pickVc = UIStoryboard(name:"Main", bundle: nil).instantiateViewController(withIdentifier: "request-VC") as! RequestViewController
pickVc.modalPresentationStyle = .custom
pickVc.requestDelegate = self
present(pickVc, animated: true, completion: nil)
}
func openRequestAlertViewController() {
let pickVc = UIStoryboard(name:"Main", bundle: nil).instantiateViewController(withIdentifier: "requestAlertVC") as! RequestAlertViewController
pickVc.modalPresentationStyle = .custom
pickVc.searchDelegate = self
present(pickVc, animated: true, completion: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
self.dismiss(animated: true, completion: nil)
}
}
func dismissRequestVC() {
let searchVc = UIStoryboard(name:"Main", bundle: nil).instantiateViewController(withIdentifier: "searching-VC") as! SearchingViewController
searchVc.modalPresentationStyle = .custom
present(searchVc, animated: true, completion: nil)
}
but it also dismiss after 20 seconds and I don't want to dismiss this. any help? This is my whole code
Create an instance for a particular viewcontroller which you want to dismiss like the following
var lvc: LoadingViewController?
func showLoadingIn(viewController: UIViewController) {
lvc = LoadingViewController() // create new instance before presentation
viewController.presentViewController(lvc!, animated: false, completion: nil)
}
func dismissLoader() {
lvc?.dismissViewControllerAnimated(true) {
print("Dismissing Loader view Controller")
}
}
You are presenting all viewControllers with the current viewController and while dismissing you are are not dismissing the currently presented viewController. Instead you are dismissing the presenter viewController so all the presented ViewControllers are also getting dismissed.
If you want to dismiss a particular viewController you can do it like this,
yourViewController.dismiss(animated: true, completion: nil)
For example in your case you can do this to dismiss the pickVc after 20 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
pickVc.dismiss(animated: true, completion: nil)
}
I am new to iOS swift. I have three ViewController.
The page-A is root controller and it will present to the page-B. It has a timer in page-B. After 5 sec, it will change View from page-B to page-C , and close the page-B at the same time.
In the ViewControll-B
class AViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
//set the timer , and chagne view to C ViewController
Timer.scheduledTimer(timeInterval: 5,
target: self,
selector: #selector(self.changeToAnswerView),
userInfo: nil,
repeats: false)
}
#objc func changeToAnswerView() {
dismissLoader()
}
func dismissLoader() {
dismiss(animated: true) {
print("Dismissing Loader view Controller")
}
}
override func viewWillDisappear(_ animated: Bool) {
//change view to Answer ViewController
let filterVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "CViewControllerID")
filterVC.modalPresentationStyle = UIModalPresentationStyle.custom
self.present(filterVC, animated: true, completion: nil)
}
}
After timer execute for 5 sec , the BViewController will dismiss itself and present to the BViewController.
But the following error will happened:
whose view is not in the window hierarchy
Did I missing something?
Question:
How to dismiss the current ViewController and change to the new ViewController in Swift?
Thanks in advance.
Here is the Working code that you can Try
Your Controller which is Dismissed and Tend to make a new controller being presented
import UIKit
class pdfVC: UIViewController
{
var timer : Timer?
override func viewDidLoad()
{
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(pdfVC.timerAction), userInfo: nil, repeats: false)
}
#objc func timerAction()
{
if timer != nil {
timer?.invalidate()
dismiss(animated: true, completion: {
print("Dismissed")
})
}
}
override func viewWillDisappear(_ animated: Bool) {
if self.isBeingDismissed {
let filterVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "demoViewController")
filterVC.modalPresentationStyle = UIModalPresentationStyle.custom
print("called")
self.presentingViewController?.present(filterVC, animated: true, completion: nil)
}
}
}
Output
Try changing dismissLoader function to something like:
func dismissLoader() {
dismiss(animated: true) {
print("Dismissing Loader view Controller")
if let presentingController = self.presentingViewController {
let filterVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "BViewControllerID")
filterVC.modalPresentationStyle = UIModalPresentationStyle.custom
presentingController.present(filterVC, animated: true, completion: nil)
}
}
}
and remove the viewWillDisappear function.
The problem is that you are trying to present the BViewController from the loader after being dismissed (i.e removed from window hierarchy) which at that point doesn't exist.
So, the solution is that you could get a reference to the presenting view controller which is presenting the loader and which will appear after dismissing the loader and present the new view controller from there.
in your B view controller after its dismissed,
in its completion handler
self.dismiss(animated: true) {
if let presentingVC = self.presentingViewController {
present c view controller here
}
}
this above is one way to do so, another way is, through delegate
in A view controller:
if let bVC = self.storyboard.instantiateViewController(withIdentifier: B.controllerIdentifier) as? B {
bVC.delegate = self
self.present(bVC, animated: true, completion: nil)
}
inside B View Controller
add delegate method for Protocol
protocol BProtocol: class {
func didClose()
}
and in B dismiss
var delegate: BProtocol?
self.dismiss(animated: true) {
self.delegate?.didClose()
}
this delegate will be implemented by A ViewController as
extension AViewController: BProtocol {
func didClose() {
//present C
}
}
You are trying use a reference (instance) of view controller, that would no longer exist in memory.
Try this
if let presentingVC = self.presentingViewController {
let filterVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "CViewControllerID")
filterVC.modalPresentationStyle = UIModalPresentationStyle.custom
presentingVC.present(filterVC, animated: true, completion: nil)
}
Note: Present a new view controller (filterVC) once your current view controller is dismissed. A view controller can present only a one view controller at time (if you choose present modally). Perform this operation after a delay of 1 second..
Edit
Try with this edited code.
Class AViewController: UIViewController {
var timer: Timer?
var presentingVC: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
//set the timer , and chagne view to C ViewController
Timer.scheduledTimer(timeInterval: 5,
target: self,
selector: #selector(self.changeToAnswerView),
userInfo: nil,
repeats: false)
}
#objc func changeToAnswerView() {
dismissLoader()
}
func dismissLoader() {
dismiss(animated: true) {
print("Dismissing Loader view Controller")
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//change view to Answer ViewController
if let presentingVC = self.presentingViewController {
self.presentingVC = presentingVC
}
}
override func viewWillDisappear(_ animated: Bool) {
//change view to Answer ViewController
super.viewWillDisappear(animated)
if let presentingVC = self.presentingVC {
let filterVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "CViewControllerID")
filterVC.modalPresentationStyle = UIModalPresentationStyle.custom
presentingVC?.present(filterVC, animated: true, completion: nil)
} else {
print("Presenting View controller is nil")
}
}
}
I have a viewControllerA that is presented programmatically without segues in storyboard, like this:
let storyboard = UIStoryboard(name: "IdStoryBoard", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "idcontroller") as! BController
self.present(controller, animated: true, completion: nil)
Then from viewControllerA I go to viewControllerB, using a storyboard segue.
When I dismmiss viewControllerB self.dismiss(animated: true, completion: nil), viewDidAppear is not triggered in viewControllerA.
If you want to trigger an event in viewControllerA when dismissing viewControllerB I suggest creating a delegate method to link both.
On your viewControllerA include the following:
protocol TriggerEventDelegate {
func eventToBeTriggered();
}
Then make your viewControllerA conform to that protocol:
extension AController: TriggerEventDelegate {
func eventToBeTriggered() {
// Implement whatever you want to trigger here
}
}
On your viewControllerB, create a delegate reference:
class BController: UIViewController {
weak var delegate: TriggerEventDelegate?
}
Implement the triggering whenever you are dismissing viewControllerB:
func dismiss() {
delegate?.eventToBeTriggered()
self.dismiss(animated: true, completion: nil)
}
Last but not least, when moving from viewControllerA to viewControllerB set your delegate with viewControllerA:
func pushFromAToB() {
let storyboard = UIStoryboard(name: "IdStoryBoard", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "idcontroller") as! BController
controller.delegate = self
self.present(controller, animated: true, completion: nil)
}
Your event will now be triggered properly without having to rely on viewDidAppear