Flutter - iOS Native Side Navigation Controller - swift

In my Flutter project, my goal was to redirect to iOS native side and I have done this. After landing on iOS native side, from flutter view controller (A), I can go to view controller (B). After this I decided to add a navigation controller on this page (B) so I Embed in that view controller (B) in a navigation controller but the navigation doesn't showed up after I was in view controller (B).
StoryBoard Image
AppDelegate Page Code
What I did so far is:
1 - Embed in Flutter view controller (A) in a navigation controller [output was: Didn't found flutter view controller when I first run the app]
2 - In AppDelegate, I did change the NavigationBarHidden to false and it starts show every where in the app.
self.navigationController.setNavigationBarHidden(false, animated: false)

I think NavigationBar have shown. While it's backgroundColor is nil.
Since UINavigationController is a native controller, it is easy to debug its view hierarchy.
The left side is its simulator. The background is the Xcode debugger.
Here is my case solved:
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
if let ctrl = window.rootViewController as? FlutterBinaryMessenger{
let methodChannel = FlutterMethodChannel(name: "_PageMineState", binaryMessenger: ctrl)
methodChannel.setMethodCallHandler { call, handler in
switch call.method{
case "pic":
if let vc = ctrl as? FlutterViewController{
let controllerOne = UIViewController()
// bar title
controllerOne.navigationControllergationItem.title = "Come"
controllerOne.view.backgroundColor = UIColor.red
let navigationController = UINavigationController(rootViewController: controllerOne)
navigationController.modalPresentationStyle = .fullScreen
// To solve it is to add:
// bar background color
navigationController.navigationControllergationBar.backgroundColor = UIColor.white
vc.present(navigationController, animated: true) { }
}
default:()
}
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

Related

Navigation after google sign in | UIKit

I am trying to Navigate to my main content after integration google sign in a separate Login Page. Before creating the login page, my app's entry point was the Navigation controller.. Now I needed users to login before that so I added a View Controller with google sign in button, and the sign in is working fine. this is my storyboard:
Now I want if the sign in succeed, to display the app's main content view (which is as if the entry point is the Navigation Controller from now on). I tried the following approach:
#IBAction func signIn(_ sender: Any) {
let signInConfig = GIDConfiguration.init(clientID: "xyz.apps.googleusercontent.com")
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
guard error == nil else { return }
// If sign in succeeded, display the app's main content View.
let vc = self.storyboard?.instantiateViewController(withIdentifier: "scanReceiptsViewController") as! ViewController
self.navigationController?.pushViewController(vc, animated: true)
self.present(vc, animated: true, completion: nil)
}
}
There are 2 problems:
the new viewcontroller is presented modally, users can very easily go back to the login page and it distrubs the flow of my app
user will need to login everytime to navigate to the main content view controller, I want that if signed in the entry point becomes the main storyboard from now on. if that makes sense
Thank you and sorry I'm new into learning swift!
I would recommend that you keep your navigation controller (I will refer to it from now on as ScanRecipientsViewController) as the default app entry point, since users only need to see the login screen if they are not logged in, which is most of the time not the case. This will give us the benefit of a slightly faster and smoother performance at the app start when the user is logged in.
You could handle this case in your AppDelegate's func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool method. For example, check if the user is logged in, if not, set your LoginController as the new root controller, which will replace your ScanRecipientsViewController that was set as root by the storyboard.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if GIDSignIn.sharedInstance.currentUser == nil { // just a guess, please refer to the documentation on how to check if the user is logged in
let storyboard = UIStoryboard(name: "Login", bundle: nil)
/*
If you use the SceneDelegate in your project, you will need to retrieve the window object from it instead of from the AppDelegate.
let window = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window
*/
window?.rootViewController = storyboard.instantiateViewController(withIdentifier: "LoginController")
}
return true
}
After the user successfully logs in, you can create a new instance of your ScanRecipientsViewController and set it as the root controller back again. You can do this by e.g. creating a public method in your AppDelegate:
extension AppDelegate {
func openRecipientsController() {
let storyboard = UIStoryboard(name: "Home", bundle: nil)
/*
If you use the SceneDelegate in your project, you will need to retrieve the window object from it instead of from the AppDelegate.
let window = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window
*/
window?.rootViewController = storyboard.instantiateInitialViewController()
}
}
and in your LoginViewController:
#IBAction func signIn(_ sender: Any) {
let signInConfig = GIDConfiguration.init(clientID: "xyz.apps.googleusercontent.com")
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
guard error == nil else { return }
// If sign in succeeded, display the app's main content View.
(UIApplication.shared.delegate as? AppDelegate)?.openRecipientsController()
}
}
As a last thing, you normally don't use both pushing and presenting at the same time, you need to choose one of the two options.
let vc = self.storyboard?.instantiateViewController(withIdentifier: "scanReceiptsViewController") as! ViewController
self.navigationController?.pushViewController(vc, animated: true)
self.present(vc, animated: true, completion: nil)
I wish you lots of fun learning iOS development with Swift!

How call LaunchScreen in applicationWillEnterForeground(_:)

When the application comes out of the background, it is necessary that the LaunchScreen is loaded after returning to the application.
Is it possible to somehow open the Launch screen through applicationWillEnterForeground(_:) ?
I tried to do through UIApplicationExitsOnSuspend - in iOS 13 Deprecated
If I understood correctly, you can display the launch screen like this:
func applicationWillResignActive(_ application: UIApplication) {
let controller = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()!
if let navigationController = self.window?.rootViewController as? UINavigationController
{
navigationController.pushViewController(controller, animated: false)
}
}
func applicationDidBecomeActive(_ application: UIApplication) {
if let navigationController = self.window?.rootViewController as? UINavigationController
{
navigationController.popViewController(animated: false)
}
}
Have to be a NavigationController in Main.storyboard

Issue with presenting viewcontroller in iOS 13, Xcode 11

I'm trying to present a viewcontroller on topMostViewController. It's working in iOS 12 and lower. But on iOS 13 I'm getting this error:
Manually adding the rootViewController's view to the view hierarchy is no longer supported. Please allow UIWindow to add the rootViewController's view to the view hierarchy itself.
I have checked on iOS 12 and lower, and the code below works fine. But on iOS 13 I am having trouble presenting the view controller. I printed on viewDidLoad; it's getting printed but the view doesn't appear.
func presentInWindow(animated flag: Bool = true, completion: (() -> Void)? = nil) {
DispatchQueue.main.async {
var alertWindow: UIWindow?
alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow?.windowLevel = UIWindow.Level.alert + 1
alertWindow?.rootViewController = UIApplication.topViewController()
if let rootViewController = alertWindow?.rootViewController {
alertWindow?.makeKeyAndVisible()
rootViewController.present(self, animated: flag, completion: completion)
}
}
}
static func topViewController() -> UIViewController? {
var topViewController: UIViewController?
if #available(iOS 13.0, *) {
topViewController = shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first?.rootViewController
} else {
topViewController = shared.delegate?.window??.rootViewController
}
while true {
if let presented = topViewController?.presentedViewController {
topViewController = presented
} else if let nav = topViewController as? UINavigationController {
topViewController = nav.visibleViewController
} else {
break
}
}
return topViewController
}
This code will work to create a view controller on top. You can adjust the size of the view controller on this line: popOverVC.view.frame = lSs I'm not sure if this code is exactly what you are asking for, but if you need a quick solution, it will present view controllers in swift 5, iOS 13, and xcode 11. Note that it is a child view controller, so if you remove the parent, it will leave too. Simply change self to ViewController that you want to present on.
let popOverVC = UIStoryboard(name: "yourSB", bundle: nil).instantiateViewController(withIdentifier: "vcYouWantID") as! vcYouWant
self.addChild(popOverVC)
let lSs = UIScreen.main.bounds
popOverVC.view.frame = lSs
popOverVC.view.tag = tag
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParent: self)
I received the same error while updating an app which worked fine previously. It looks like the new requirement for AppDelegate.swift is:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) { }
}
hth
I encountered the same issue tonight. I tried the methods suggested by people on the Net including Stackoverflow, but none had worked.
Just now, as if magic was working, I tried to form an outlet from the ViewController (visually the "background" layer for all subviews) in the Main.storyboard to the ViewConstroller class in ViewController.swift. It worked in my project for iOS 13 simulator in Xcode 12.2.
Hope this trick will also work for you.
I found this to fix my problem (in old Objective-C code):
// old way broken in iOS 13
//appDelegate.window.rootViewController = vc;
// this works in iOS 13
[appDelegate.window setRootViewController:vc];

Present login view controller modally in swift tabbed application

I'm trying to present a view controller modally from my app delegate, however, I'm receiving the below error. When creating my project I chose a tabbed application. I need help resolving this error and presenting the view controller. Would also appreciate code to dismiss the view controller too
2018-10-03 20:28:57.324 App[74397:2825933] Warning: Attempt to present <App.SignUpViewController: 0x7fbd7b608c60> on <UITabBarController: 0x7fbd7b407620> whose view is not in the window hierarchy!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
showSignUpView()
return true
}
func showSignUpView() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let signUpViewController = storyboard.instantiateViewController(withIdentifier:"SignUpViewController") as! SignUpViewController
signUpViewController.modalPresentationStyle = UIModalPresentationStyle.fullScreen
signUpViewController.modalTransitionStyle = UIModalTransitionStyle.coverVertical
window?.rootViewController?.present(signUpViewController, animated: true, completion: nil)
}

how to use 'presentViewController' with cover View and main page

A lots app, they have the cover view, like facebook, wechat...
but when I try to use presentViewController let the cover present to main page, that still in the cover vie. The following is my code:
var window: UIWindow?
var coverVC:CoverView?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.backgroundColor = UIColor.lightGrayColor()
coverVC = ViewController()
coverVC?.refresh(window!.frame)
window!.makeKeyAndVisible()
return true
}
Coverview
class CoverView: UIViewController {
func refresh(frame:CGRect){
self.view.frame = frame
self.view.backgroundColor = UIColor.whiteColor()
var mainVC:mainView = mainView()
mainVC.refresh(frame)
self.dismissViewControllerAnimated(false, completion: nil)
self.presentViewController(mainVC, animated: false, completion: nil)
}
}
Main view:
class mainView: UIViewController {
func refresh(frame:CGRect){
self.view.frame = frame
self.view.backgroundColor = UIColor.blueColor()
}
}
Maybe can use other easy way to achieve the goal, that's ok.
otherwise, please don't use "UInavigationcontroller", I know that can present view, but I don't want the bar in cover view, thanks.
I find the correct with cover/login view, it is called SplashScreen.
And in xcode 9, you can find at [TARGETS]>[APP icons and Launch Images] (Image).(The past version, you need add "launch image" in plist file.)
Otherwise, If you doesn't want use this view, just delete "LaunchScreen".