I need to run a piece of code when user dissmisses the UIReferenceLibraryViewController. But my code that I previously coded isn't working on iOS 13.
Here is the code I wrote for iOS 12:
override func viewDidLoad() {
if UIReferenceLibraryViewController.dictionaryHasDefinition(forTerm: word) {
let ref: UIReferenceLibraryViewController =
UIReferenceLibraryViewController(term: word)
ref.reactive
.trigger(for: #selector(onboardNav.viewDidDisappear(_:)))
.observe { _ in self.handleModalDismissed() }
self.present(ref, animated: false, completion: nil)
}
}
func handleModalDismissed() { // I need to run this function when user presses "Back" button
self.showAlert(error: false, word: "")
}
I just found an answer! You need to subclass a UIReferenceLibraryViewController, and override viewWillDisappear. Like so:
class ReferenceLibraryViewControllerWithDismiss: UIReferenceLibraryViewController {
override func viewWillDisappear(_ animated: Bool) {
// your code
}
}
And then present ReferenceLibraryViewControllerWithDismiss:
let vc = ReferenceLibraryViewControllerWithDismiss()
self.present(vc, animated: true)
Happy coding!
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 function call (pop view) in my 1st view controller which have to be called only once in app. Since then whenever I return back to 1st View controller the function need not to be called again.
func popView() {
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "popView") as! popView
self.addChild(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParent: self)
}
I have tried the following code and previous other sources in stack overflow, didn't work though..
///// Once Action in View Controller
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if self.isBeingPresented || self.isMovingToParent {
// Perform an action that will only be done once
popView()
}
}
Maybe this works. In your ViewController, add a static property:
static var shouldPop = true
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if isBeingPresented || isMovingToParent {
// Perform an action that will only be done once
if (type(of: self).shouldPop) {
type(of: self).shouldPop = false
popView()
}
}
}
Of course, depending on your setup, this won't work if you have more than one instance of this viewcontroller that should keep their own state on whether popView should be called or not.
You should call this in viewDidLoad method. It's called once per UIViewController life cycle.
Documentation here.
Just like this:
override func viewDidLoad() {
super.viewDidLoad()
if self.isBeingPresented || self.isMovingToParent {
// Perform an action that will only be done once
popView()
}
}
If your way is pop view after you was once in view controller you could do like this:
/// bool that help indicate your visit
var isViewControllerVisited = false
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if isViewControllerVisited {
// Perform an action that will only be done once
popView()
}
//change it here
isViewControllerVisited = true
}
Hope it's help!
If you want to call that PopView function only once in you App then try this,
In App delegate, set bool value
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UserDefaults.standard.set(true, forKey: "showPop") // like so
return true
}
Then, in first view controller try this,
func hasLaunchPop() {
let isshowPop: Bool = UserDefaults.standard.bool(forKey: "showPop")
if isshowPop == true {
popView()
UserDefaults.standard.set(false, forKey: "showPop")
}
}
then in viewdidload call like this,
override func viewDidLoad() {
super.viewDidLoad()
hasLaunchPop()
}
So that PopView appears only once in your app when its launched and will never show up again.
For me I prefer to use lazy loading. This allow not to write any logic, just need to use Swift lazy var declaration. Something like this:
private lazy var viewDidAppearOnce: Bool = {
popView()
}()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
_ = viewDidAppearOnce
}
Below code, try it..
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UserDefaults.standard.set(false, forKey: "isPopOverVCPopped")
if UserDefaults.standard.bool(forKey: "isPopOverVCPopped") == false {
UserDefaults.standard.set(true, forKey: "isPopOverVCPopped")
popView()
}
}
As per the view controller's lifecycle, the viewDidLoad method only gets called once. so you should call your method only there.
Or you can try the following code:
var isScreenAppeared = false
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !isScreenAppeared {
popView()
}
isScreenAppeared = true
}
I have created a CustomAlertView.
protocol CustomAlertViewControllerDelegate{
func retryToFetchData()
}
class CustomAlertViewController: UIViewController {
#IBOutlet weak var alertView: UIView!
var delegate: CustomAlertViewControllerDelegate!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.alertView.alpha = 0
self.alertView.frame.origin.y = self.alertView.frame.origin.y + 50
UIView.animate(withDuration: 0.4) {
self.alertView.alpha = 1.0
self.alertView.frame.origin.y = self.alertView.frame.origin.y - 50
}
}
#IBAction func retryButton(_ sender: UIButton) {
delegate?.retryToFetchData()
self.dismiss(animated: true, completion: nil)
}
}
I have created a static function to show that view. The view is just a UIViewController that will have a child View which will act as a popUP, with a transparent background.
func showErrorBox(view: UIViewController, message: String, delegate: CustomAlertViewControllerDelegate){
let customAlert = view.storyboard?.instantiateViewController(withIdentifier: "customAlertViewController") as! CustomAlertViewController
customAlert.providesPresentationContextTransitionStyle = true
customAlert.definesPresentationContext = true
customAlert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
customAlert.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
customAlert.delegate = delegate
view.present(customAlert, animated: true, completion: nil)
}
Now, I am calling this VC anywhere I needed to show a popUP with showErrorBox(view: self, message: message, delegate: self) now my issue is I need to show this popup in a ViewController which is inside of a TabBarController, when I change the view between the UITabBar, and try pressing the reload button, the app throws an error,
presentViewController does not work: view is not in the window
hierarchy
Edit:
There is a Retry button on the ErrorBox(popUp). The error happens only when the error box is loaded and I changed the view to different tab and then hit the reload button. In normal scenario like, hitting the reload button when I am in the same page works fine.
I am not sure of issue, but it has something to do with when I change the view between tabs when the error box is present.
try self.present(customAlert, animated: true, completion: nil) instead view.present(customAlert, animated: true, completion: nil). If I understand your problem, should works
Solution,
The problem was, when I changed the views in TabController the reference to the customAlertView was removed from the window hierarchy. So, As soon as I leave the TabView which was loading the CustomAlertView need to be removed.
So to solve the issue, I have added following code in CustomAlertViewController
override func viewWillDisappear(_ animated: Bool) {
super. viewWillDisappear(animated)
self.dismiss(animated: true, completion: nil)
}
Try to getting the TopMost ViewController and presenting CustomAlertViewController from it instead of "self".
refer this link to :
GetTopMostViewController
As SplitViewController loads, I am showing a Login Screen. On successful login, I need to go back to parent view controller. Somehow dismissal is not working for me. Here is the code:
ParentViewController:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if !appDelegate.loggedIn {
self.performSegueWithIdentifier("loginScreen", sender: self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
Child ViewController:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.loggedIn = true
self.dismissViewControllerAnimated(true, completion: nil)
The dismissal part never works. It just hangs on Login Screen.
Try one of the following:
1) remove self. keep only dismissViewControllerAnimated(true, completion: nil)
or remove self. and make it:
2) presentingViewController.dismissViewControllerAnimated(true, completion: nil)
or remove self. and try:
3) presentedViewController.dismissViewControllerAnimated(true, completion: nil)
Try this in your parent view controller:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if !appDelegate.loggedIn {
let loginVC: UIViewController = self.storyboard!.instantiateViewControllerWithIdentifier("LoginViewController") as UIViewController
loginVC = UIModalTransitionStyle.CoverVertical
self.parentViewController?.presentViewController(loginVC, animated: true, completion: nil)
}
}
You're instantiating the new view controller by its own name rather than by the segue name.
So I'm building an app for the first time and I am using https://github.com/watsonbox/ios_google_places_autocomplete. My code is down below.
func testFunction() {
println(gpaViewController)
let view2 = self.storyboard?.instantiateViewControllerWithIdentifier("view2") as! ViewController2
self.showDetailViewController(view2, sender: self)
}
And in the extension ViewController I call the testFunction:
extension ViewController: GooglePlacesAutocompleteDelegate {
func placeSelected(place: Place) {
println(place.description)
place.getDetails { details in
println(details)
}
self.dismissViewControllerAnimated(true, completion: nil)
println(self)
testFunction()
}
func placeViewClosed() {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Basically, I want to switch views after I click on the tableView cell. However, nothing happens. But when I attach the testFunction to IBaction and a button, it takes me to another viewController.