How do I apply present working properly in swift - ios

I have trouble in my application with swift 4.
I'd like to move my view controller automatically when my string get a value without nil. (Promatically)
However it does not work at all.
if(rst_str != nil){
let nextView = storyboard?.instantiateViewController(withIdentifier: "viewListController") as! ViewListController
//viewListController.delegate = self;
print("log storyboard");
self.present(nextView, animated: true, completion: nil);
}
The message of "log storyboard" is printed normally.
However the line of self.present is not working at all.
How can I resolve from my issue?

The line
self.present(nextView, animated: true, completion: nil);
won't work in 2 cases
1- the current vc you run this line in is currently not fully laid - out such as making it in viewDidLoad
2- the current vc is currently presenting another vc

Related

Modul should show modal/UIViewController in main thread but it doesnt work without delay

I trying to present UIViewController in my React native app from Swift module
I am presenting it like this
let screen = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
screen?.rootViewController?.present(payController!, animated: true, completion: nil);
and I get this error:
UIApplication.windows must be used from main thread only
ok I have to add it to my thread
DispatchQueue.main.async {
let screen = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
screen?.rootViewController?.present(payController!, animated: true, completion: nil);
}
and it works fine when I call this function with small delay
setTimeout(() => {
showMyWiew();
}, 500);
or delay into swift like this
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let screen = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
screen?.rootViewController?.present(payController!, animated: true, completion: nil);
}
but if I remove the delay then this modal is not shown. But it should be there. and i see this in log in swift which confirms my theory:
[Presentation] Attempt to present <PKAddPaymentPassViewController: 0x103d6b2e0> on <UIViewController: 0x103c25480> (from <UIViewController: 0x103c25480>) which is already presenting <RCTModalHostViewController: 0x10e21df40>.
PKAddPaymentPassViewController is my UIViewController
I don't know how to fix this issue...
UPDATE
Based on first comment I did this
DispatchQueue.main.async {
let screen = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
let controller = screen?.rootViewController?.presentedViewController;
if controller == nil {
screen?.rootViewController?.present(payController!, animated: true, completion: nil);
} else {
screen?.rootViewController?.presentedViewController?.present(payController!, animated: true, completion: nil)
}
}
and the good news is the modal will show up but immediately is close I see the open effect and then close effect ...
SOLUTION
all in the update section
refactoring RN code
It turned out There is open another modal before this new. The old modal is not closed before I open the new one ... so I close the old modal faster and then I show new and all is good
That is because at that exact moment screen?.rootViewController seems to already present another ViewController (RCTModalHostViewController). A VC that is already presenting some VC can not present yet another VC. What you could do is to call present on the VC that is being presented by the rootViewController at that moment: screen?.rootViewController.presentedViewController?.present(...). (Presenting on a presented ViewController does work. Presenting on a VC that presents another VC does not.)
If you do that you should also make sure that presentedViewController is actually set and not nil. If it is nil you can just call present on rootViewController as you did earlier.

Swift iOS - how to properly use popToViewController

it's again me - learning swift.
Question is simple, i have control views like this:
[Initial]->[NotLogged]->[SignUp]
Now after sign up i have double dismiss, it's really ugly!
I wan't to go straight from SignUp to Initial page.
I tried this code but sadly it closing app without any error.
self.dismiss(animated: true, completion: {
let controllers = self.navigationController?.viewControllers
for vc in controllers! {
if vc is InitialViewController {
_ = self.navigationController?.popToViewController(vc as! InitialViewController, animated: true)
}
}
} )
pushViewController works same as above, there is output:
2020-01-29 20:30:38.342180+0100 BillyBill[44355:19540995] Can't end
BackgroundTask: no background task exists with identifier 10 (0xa), or
it may have already been ended. Break in
UIApplicationEndBackgroundTaskError() to debug.
Simply just pop to root view controller
self.navigationController?.popToRootViewController(animated: true)
You can just use self.navigationController?.popToRootViewController() command in place of
for vc in controllers! {
if vc is InitialViewController {
_ = self.navigationController?.popToViewController(vc as! InitialViewController, animated: true)
}
}

How to solve this error "Warning:Attempt to present whose view is not in the window hirearchy"?

I'm developing an iOS application but I don't have storyboard and I do pure swift code, when I try to check the authentication in the MainViewController and I use perform to go to another ViewController and if token doesn't exist show button in MainViewController I ran into this warning and it won't work.
when I use perform like the snippet below to go to another ViewController by clicking on a button it works just fine.
I've seen all the answer by the title I'm asking here but all the examples has storyboards so it's not related to my question here.
here's a snippet that I'm trying to do in my app.
if defaults.string(forKey: Constants().userTokenKey) != nil
&& defaults.string(forKey: Constants().userTokenKey) != "" {
print("YOU ARE IN ELSE!")
let vc = SelectLocationOnMapViewController()
UIApplication.topViewController()?.present(vc, animated: true, completion: nil)
} else {
UIView.animate(withDuration: 1, animations: {
self.loginRegisterParentView.alpha = 1.0
})
setButtonActions()
}
The problem is here
UIApplication.topViewController()?.present(vc, animated: true, completion: nil)
it seems you use the rootVC which isn't yet fully presented to present another Vc , you need to move this code from viewDidLoad to say viewWillAppear/viewDidAppear

Swift ios error in navigating back

I have use the following code below . to navigate but i have tried all possible idea i have to navigate back but i always received an error. like Warning: Attempt to present on whose view is not in the window . when using the code below what is the best way to navigate back ? or to go to another view?
if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ScoreViewController") as? ScoreViewController
{
present(vc, animated: true, completion: nil)
}
If you are using NavigationController to push one ViewController to other then for navigate back you have to use Pop like this,
self.navigationController?.popViewController(animated: true)
and if you are using Modal Controller to present another Controller then for navigate back you have to use dismiss like this,
dismiss(animated: true, completion: nil)
Let me know in case of any queries.
You are presenting the view controller not pushing it. To come back to the controller, write down the below controller:-
dismiss(animated: true, completion: nil)

How to connect Universal Links to UIViewController?

*Disclaimer: I've only been coding in iOS/XCode/Swift for a couple of weeks
I have Universal Links working in that clicking a link outside my app opens up the app and I catching the Url in the AppDelegate class.
So that's all good.
My question is... how do I then redirect to the correct UIViewController AND pass the controller some info from my URL? All Universal Link tutorials stop before they get to that part.
In particular I'm confused about the lifecycles of AppDelegate and how it relates to UIViewController.
My app had two UIViewController sitting under (is this right?) a UINavigationController.
What I've tried
I have tried handling the url event in AppDelegate, and setting a public property, and then in my ViewController getting access to the AppDelegate. HOWEVER, after the Universal Link is clicked, both viewDidLoad and viewWillAppear don't get called again :-/
What's the best way to redirect to a ViewController from AppDelegate? My goal is simply to load the root view controller BUT I need to pass in some data from the URL. How?
First, Read your URL.
Get your parameters from URL
Initiate your target controller
Set your parameter to that controller
Present controller on root view controller
let urlString = url.absoluteString
let queryArray = urlString.componentsSeparatedByString("/")
if queryArray[2].lowercaseString == "yourQuery" {
let queryId = Int(queryArray[3])
if self.window?.rootViewController?.presentedViewController != nil {
self.window?.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
}
let queryVC = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier(QUERY_SCENE) as? QueryViewController
queryVC?.urlQueryId = queryId!
self.window?.rootViewController?.presentViewController(queryVC!, animated: true, completion: nil)
}
Edit:
Push a controller say 'PresentedViewController' on navigation controller and if rootViewController is also navigation controller
And on back press on controller 'OnBackPressViewController' present controller 'PresentedViewController'
if self.window?.rootViewController?.presentedViewController != nil {
self.window?.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
}
let navController = self.window?.rootViewController?.storyboard?.instantiateInitialViewController() as? UINavigationController
let presentedVC = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier(PRESENTED_SCENE) as? PresentedViewController
//Pass parameters to PresentedViewController
let onBackPressViewController = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier(ON_BACK_PRESS_SCENE) as? OnBackPressViewController
navController!.pushViewController(onBackPressViewController!, animated: true)
navController!.pushViewController(presentedVC!, animated: true)
self.window?.rootViewController?.presentViewController(navController!, animated: true, completion: nil)
You can update your code according to that. Always remember that you have to present any view controller on rootViewController.
It depends on your app architecture. You can definitely assume that the AppDelegate has the reference to the rootViewController (window?.rootViewController). You should know what's the type of that controller. Then you can access to the other and so on. This question is, anyway, too much generic in my opinion.

Resources