I am creating an application using xamarin.ios. I have some pages in my storyboard and I have set "Is initial View Controller" for one of the pages as the first page.
Users see that page as the first page if they already logged in otherwise I want to show the login page. To do that I added this code:
[Export ("application:didFinishLaunchingWithOptions:")]
public bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
this.Window = new UIWindow(UIScreen.MainScreen.Bounds);
LoginViewController yourViewController = UIStoryboard.FromName("Main", NSBundle.MainBundle).InstantiateViewController("LoginViewController") as LoginViewController;
this.Window.RootViewController = yourViewController;
this.Window.MakeKeyAndVisible();
return true;
}
The problem is, this code cannot change the first page at all. It seems my application always ignore this code and shows the page with "Is Initial View controller" equals true on storyboard. It means when I change first page on storyboard I see the change but no change when I set it in my code.
The app delegate’s role changes from iOS 12 to iOS 13, the SceneDelegate is responsible for setting up the scenes of your app :
[Export ("scene:willConnectToSession:options:")]
public void WillConnect (UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
{
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see UIApplicationDelegate `GetConfiguration` instead).
UIWindowScene myScene = scene as UIWindowScene;
Window = new UIWindow(myScene);
UIViewController viewController = new UIViewController();
UINavigationController navigationMain = new UINavigationController(viewController );
Window.RootViewController = navigationMain;
Window.MakeKeyAndVisible();
}
Here are some threads about this question in swift: set-rootviewcontroller-ios-13 and ios-13-swift-set-application-root-view
And articles: accessing-root-view-controller-ios13-scenedelegate and scene-delegate-app-delegate-xcode-11-ios-13
Related
I have an app with a custom tab bar controller, and a data model powered by a stateController. I want to share data from my model across the tabs.
Few callouts:
My app uses both storyboards & programmatic (Swift 5). Assume storyboard here
When the app loads, I check userDefaults to see if it's the first app launch. If it is, firstTimeVC is the rootVC
If it's not, my Tab Bar is the rootVC. And this logic lives in the Scene Delegate
I'm using Dependency Injection to create a shared instance of my data model across tabs. People suggest injecting the data model into my TabBar & childVCs using the App / Scene Delegate. Problem is, the TabBar isn't always the rootVC.
My questions are:
Can I inject my data model into the TabBar's child VCs directly from the TabBar? Instead of the Scene Delegate?
If I use Scene Delegate, then how do I inject the data model into the TabBar's childVCs when the TabBar is NOT the rootVC (ie first app launch)
Or should the TabBar always be the rootVC, and I move the first-launch-check to a childVC instead of Scene Delegate
What's the right way to do this?
My first time app launch check (currently inside Scene Delegate):
if isFirstLaunch {
rootViewController = "FirstTimeUserViewController"
} else {
rootViewController = "TabBarController"
}
window?.rootViewController = rootViewController
window?.makeKeyAndVisible()
My dependency injection (currently inside Scene Delegate):
let tabBarController = storyboard.instantiateViewController(withIdentifier: "CustomTabBarController") as! CustomTabBarController
for child in tabBarController.viewControllers ?? [] {
if let top = child as? StateControllerProtocol {
top.setStateController(stateController: stateController)
}
}
A screenshot of my storyboard & VC setup:
I am running 2015 Visual Studio with Xamarin. When I launch the iOS Simulator, it is showing a black screen on the simulator. I am using a Storyboard with a Navigation Controller.
I have the Main Interface set to the my storyboard.
Size Class - Any / Any
I have restarted the simulator, ran "Reset Content and Settings"
AppDelegate code (only method with contents.):
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
// If you have defined a root view controller, set it here:
Window.RootViewController = new Controllers.RegistrationController();
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
You could try something like :
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
string storyboardName = "Main";
UIStoryboard storyboard = UIStoryboard.FromName(storyboardName, null);
// if it is the first viewcontroller
UIViewController vc = storyboard.InstantiateInitialViewController();
// If you have defined a root view controller, set it here:
Window.RootViewController = vc;
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
if your storyboard is called Main.stroyboard try using this:
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
return true;
}
I tested both of these options and both should work when your Storyboard is called Main
You need to have a segue from your UINavigationController to an UIViewController with the content in it. If you don't have a segue then your app is going to show a black screen since the app doesn't think it has any content.
If I understand you correctly, your storyboard may look like this: Storyboard 1
And you need to create a segue between the two view controllers so that your storyboard looks like this with the segue-arrow between the two view controllers: Storyboard 2
While using Typhoon I came across this issue, but first some background.
I'm using a storyboard.
The storyboard starts in a home screen, then flows to login, then to the main screen (UITabBarController). I use navigation controller as root controller.
If the user is already logged in I want to show the main screen without showing the home or login screens. This could be done in the home screen viewDidLoad (other suggestions welcome).
The few things i've tried are:
Using a segue from the home to the tabController but the home screen can be seen and the transition is animated (don't want this).
Instantiating the tab controller (as below) from the storyboard but the dependencies are not injected. I understand that this is because the Typhoon storyboard is not used.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc1 = [storyboard instantiateViewControllerWithIdentifier:#"MyAuth"];
I also tried using factory with Typhoon for the storyboard.
public dynamic func storyboard() -> AnyObject {
return TyphoonDefinition.withClass(TyphoonStoryboard.self){
(definition) in
definition.useInitializer("storyboardWithName:factory:bundle:"){
(initializer) in
initializer.injectParameterWith("Main")
initializer.injectParameterWith(self)
initializer.injectParameterWith( NSBundle.mainBundle() )
}
definition.scope = TyphoonScope.Singleton; //Let's make this a singleton
}
}
///Injection for tabbar controller
public dynamic func tabBarViewController() -> AnyObject {
return TyphoonDefinition.withClass(TabBarViewController.self){
(definition) in
}
}
On the viewDidLoad I push the tabBarViewController (using the injected assembly) to the navigation controller but it doesn't have the tabs as specified on the storyboard.
func viewDidLoad() {
super.viewDidLoad()
if(userLoggedIn){
self.navigationController?.pushViewController(self.injectedAssembly.storyboard().tabBarController(), animated: false)
}
}
Thanks,
ad 1. You can disable animations on segues you created in a storyboard by selecting the segue in the storyboard editor, switching to the attributes inspector of the segue and disabling the "Animates" checkbox.
ad 2. If the UIViewController which contains the code you posted was instantiated within a Typhoon injected UIViewController (for instance if you have this code within the home viewcontroller, you use plist integration, and the home viewcontroller is set as the initial viewcontroller in your storyboard), then you can access self.storyboard in the UIViewController. This storyboard will be a TyphoonStoryboard, therefore it will work.
ad 3. Just because you give Typhoon instructions on how to create your MainStoryboards and TabBarViewControllers, it doesn't mean Typhoon knows it should combine one with the other. Try using one of the withFactory: methods provided by TyphoonDefinition to instantiate your UIViewController using the correct storyboard (sorry for Obj-C instead of Swift)
- (MYViewController *)myViewController {
return [TyphoonDefinition
withFactory:[self storyboard]
selector:#selector(instantiateViewControllerWithIdentifier:)
parameters:^(TyphoonMethod *factoryMethod) {
[factoryMethod injectParameterWith:#"MYViewControllerIdentifier"];
}
configuration:^(TyphoonFactoryDefinition *definition) {
definition.classOrProtocolForAutoInjection = [MYViewController class];
}];
}
My first iOS app (utility) release is nearing completion but one issue remains: The app runs an automated sequence. On most other platforms, the sequence would complete/fail/cancel then would be followed by clean up and exit(x).
I realise that my iOSapp should not exit() so it returns to the UIApplicationDelegate where it performs the clean up; setting all of the controllers to nil (using ARC), leaving only the appDelegate instance standing.
The app then should re-instanciate the initial view controller, effectively starting the app again.
What call from the UIApplicationDelegate does this? I expect that it should be the same as that called by iOS upon initial storyboarded app startup.
You should define a (public) method in your application delegate class and call it when necessary. In that method, you should re-instantiate your initial view controller and set it as root view controller of your UIWindow instance (you should have an ivar with it).
AppDelegate.h:
- (void) resetApp;
AppDelegate.m:
- (void) resetApp {
TopViewController* controller = [[TopViewController alloc] init];
_window.rootViewController = controller;
}
Edit: If you are usign storyboards, this code works:
AppDelegate.swift (because it's 2017):
func resetApp() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let newRoot = storyboard.instantiateInitialViewController() else {
return // This shouldn't happen
}
self.window?.rootViewController = newRoot
}
(This assumes your app's initial storyboard -the one specified in Info.plist- is called "Main.storyboard")
If your app is designed in such a way that long or asynchronous operations might be in progress when this reset happens, additional considerations should be taken to deal with them. For starters, all view controllers that aren't implemented as singletons or retained by such long-lived objects will likely be deallocated.
I am creating a master detail application using monotouch for iPad. In the master view I added, a custom UIViewController. This UIViewController has a tool bar at the top and 2 UITableView. I can only see the first UITableView. I cant see the tool bar and the other UItableView at the bottom.
I am not sure if I need to turn on anything or configure anything to enable the visibility.
I created outlet for each of the table views and toolbar.
I would appreciate if anyone could shed some lights on this.
Please see the image.
Thanks
Balan Sinniah
UPDATE : I have AppDelegate code as below
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
// class-level declarations
UIWindow window;
UISplitViewController splitViewController;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
// create a new window instance based on the screen size
window = new UIWindow (UIScreen.MainScreen.Bounds);
var controller = new RootViewController ();
var navigationController = new UITabbedViewController();
var detailViewController = new UIDetailViewTabbedBarController();
splitViewController = new UISplitViewController ();
splitViewController.WeakDelegate = detailViewController;
splitViewController.ViewControllers = new UIViewController[] {
navigationController,
detailViewController
};
window.RootViewController = splitViewController;
navigationController.DetailViewController = detailViewController;
// make the window visible
window.MakeKeyAndVisible ();
return true;
}
}
My Navigation Controller is UITabbedView Controller which has 2 UIViewController. I am adding the toolbar and 2 Table Views in one of the UIViewController.
I got it working by adjusting the autosizing section in interface builder, mark the left, right and upper red lines and unmark the bottom red line, then everything looks fine to me.
I did the same for the UITableView , I umarked the red line in top.
For the toolbar, try once to implement this on your uiviewcontroller
(it can be the other way round to (so false in the first and true in the second)
public override void ViewWillAppear (bool animated) {
base.ViewWillAppear (animated);
this.NavigationController.SetNavigationBarHidden (true, animated);
}
public override void ViewWillDisappear (bool animated) {
base.ViewWillAppear (animated);
this.NavigationController.SetNavigationBarHidden (false, animated);
}
For the table, are the 2 tableview listed underneath each other?
(in the viewbuilder make the first tableview less high, it will adapt to the amount of data automatically when you run the application )