I recently refactored an app to use a UINavigationController and pushViewController(...) to transition back and forth between the main UIViewControllers of my app. Previously I was using present(...) with no issues. The app is a music player and has the appropriate background mode.
The transition to view controller B from view controller A happens after a 5 second countdown and thus can happen in the background if the user hits their home button or turns the screen off.
The issue is that the push, and all relevant events that are supposed to kick off when the view did appear, do not fire while the app is in the background, and immediately fire as soon as I turn the display back on or bring the app into the foreground.
Is there any way to force the push to happen even if the app isn't visible, or a different method to push and display the view controller?
Thank for any insight!
let nav = self.parent as! UINavigationController
print("Attempting to .pushViewController...")
nav.pushViewController(sessionVC, animated: false)
print("After .pushViewController...")
Related
I have a Xamarin Forms app. The iOS app presents a modal view controller like so:
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(viewController, true, null);
The current view goes into the background and the modal view is presented. When that modal view is dismissed, it goes away but the view that went in the background stays there. I'm not sure how to bring it back to the front and make it active.
FYI, the viewController in this case is a native iOS UIViewController inside a framework. When used inside a native iOS project in Xcode, the modal view dismisses properly and the old view returns to the foreground.
Any thoughts?
I have an Apple TV app that starts out with a splash page. It plays a short video and does some setup networking. After these both finish I automatically transition the user to a Landing page for them to sign up for the service.
let langingPageVC = LandingPageViewController(nibName: nil, bundle: nil)
self.present(langingPageVC, animated: true, completion: {
print("Transferred to landing page")
})
The problem occurs when the user hits the menu button from this point. They are automatically taken back to the splash page, and the app just displays the static image it display while waiting for the networking to complete and stays there. The user can then press the menu button again and exit the app. If the user re-enters the app, they will be on this static image and not be able to do anything.
How can I make it so the app just goes to background directly from my landing page and doesn't go back to the Splash page? I know how to detect the menu press with a gesture recognizer, but there doesn't seem to be a call I can make that will background the application from that point.
I have tried removing the Splash page from its parent in the completion block and that didn't work. I saw something about setting the page as the keyWindow but I got a message in the log saying not to do that and that the system is supposed to handle it, and it also didn't work.
Any help is appreciated.
Two possible options:
Instead of presenting LandingPageViewController from SplashViewController, you could set LandingPageViewController as rootViewController. That way your splash will not be in your backstack of viewcontrollers.
Present SplashViewController in a separate window over LandingPageViewController. Once your video finishes you can dismiss the window.
Is it possible to pop view controller from navigation controller in iOS while the app is in background?
I wrote a background task. When I switch my app to background, some code can still be executed but vc.navigationController?.popToRootViewController(animated:false) doesn't have any effect. I'm sure it's executed.
UI Transition is only working in main thread I think.
So, Your pop to root is in background thread.
This may cause your application crash some time.
I think the only solutions is popToRoot when you're about to let your app go to background. Handle the delegate from Appdelegate to do it.
Background
Out app has a feature to launch background using Significant-Change Location Service(https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/using_the_significant-change_location_service).
This an iOS feature that when the app detect significant change in location, the app launched on background. It launches directly to background state so the user won't notice that app has launched(unless writing code to notify to users. ie. sending a local push or etc.) however app is fully launched so didFinishLaunchingWithOptions method on App Delegate is called upon launch.
Situation
When our app is launched by Significant-Change Location, we send local push notification to the user notifying that how much distance the user has walked recently(we have implemented pedometer to our app). At this point, our app is launched on background.
A user may tap the local notification just received and then our app's lifecycle state will be changed to foreground and app's home screen will be displayed to the user(from user point of view app is LAUNCHED at this point).
On home view controller, we observe UIApplication.didBecomeActiveNotification and when detecting this notification, we make an API request. API response may have an award field(nullable) and when the award is not null, we show the award received modal.
// Observe app state changes to active
let center = NotificationCenter.default
center.addObserver(
self,
selector: #selector(callRewardAPI), // calls api when detecting notification
name: UIApplication.didBecomeActiveNotification, // <- didBecomeActiveNotification
object: nil
)
func callRewardAPI {
usecase.callRewardAPI)()
}
// Show modal when received API response
extension: HomeViewController, HomeUseCaseOutput {
func didReceiveReward() {
let vc = RewardReceivedViewController()
vc.modalTransitionStyle = .crossDissolve
vc.modalPresentationStyle = .overFullScreen
self.present(vc, animated: true, completion: nil)
}
}
Problem
Expected
We expect to see the modal animation of RewardReceivedViewController.
Actual
When the app has finished launching(from user point of view, from application point of view it has been launched on background and moving to foreground), the modal had already been displayed.
We are quite sure that modal had finished displayed upon launch since view displayed on the zoom animation of our app launch(the iOS animation by apple that app's view zooms in to fill the device screen on app launch) shows the modal being displayed.
Something we are confused is that this does not happen all the time but rathe happens several times and then stopped happening(behaving as is should be) for several times. We checked this on same git commit.
Something we tried
We tried to simulate the problem by triggering modal display right after receiving UIApplication.didBecomeActiveNotification.
func callRewardAPI {
// Call didReceiveReward right away to show modal.
self.ouptut.didReceiveReward()
/*
self.rewardAPI.request { [weak self] result in
switch result {
case .success(let reward):
self.ouptut.didReceiveReward()
case .failure:
// error case
}
}
*/
}
Since it seems we cannot use Xcode debug breakpoint when simulating app launch from not running to background status, we set local push notification to each point of code to observer what is happening.
What we found out is that code to show modal is called after viewDidAppear. viewDidAppear is the last lifecycle method of the view controller when launching a view controller so we're stuck finding out the solution.
I have "Tinder" like swipping view that is located in a CardViewController. The card View Controller is accessed by moving through two other view controllers. i.e. Load App -> FirstViewController -> SecondViewController - > CardViewController.
When I am in the Card ViewController and I go into background mode, the app launches on the FirstViewController and on going to the cards, they are loaded from the first card in a stack of about 10?
Is there anyway to load the app from the last Card I had swipped and in the CardViewController without having to navigate from the FirstView Controller again?
I would really appreciate the help as it's horribly affecting some of my users.
An example of a Tinder like card view is shown!
The problem, from the sound of it, is not what happens when the app goes into the background — that would leave it in exactly the same state when it reactivates. The problem is what happens when the app goes into the background and quits. Your app is then relaunched from scratch, which is why you find yourself in the first view controller. What's upsetting you is the difference between the app's behavior in these two situations.
Apple provides a solution to this situation: UIViewController, along with the App Delegate, has methods permitting you to save and restore state. When the app goes into the background, the current configuration (what view controller's view is showing) is saved. That way, even when the app quits, when it relaunches it can get back to that configuration before it appears to the user. Thus, coming back from background-and-quit looks just like coming back from mere backgrounding.
For full details, see Apple's documentation. This is a good place to start:
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/PreservingandRestoringState.html