NavigationController is null after setting RootViewController in Xamarin.iOS - c#

I am using storyboards with Xamarin.iOS. The DashboardViewController (a UINavigationController) is set as the initial view in the storyboard. However, in my AppDelegate class, the FinishedLaunching method is conditionally checking if the user needs to login or not when the app starts up. If so, it will set the "ViewController" (login controller) as the RootViewController, or else it will instantiate the initial view controller as set by the storyboard, which is the "DashboardViewController". Here is the code for the FinishedLaunching method.
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
// Override point for customization after application launch.
// If not required for your application you can safely delete this method
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var storyboard = UIStoryboard.FromName("Main", NSBundle.MainBundle);
bool isLoggedOut = true;
UIViewController rootViewController;
if (isLoggedOut)
rootViewController = (UIViewController)storyboard.InstantiateViewController("ViewController");
else
rootViewController = (UIViewController)storyboard.InstantiateInitialViewController();
Window.RootViewController = rootViewController;
Window.MakeKeyAndVisible();
}
Once the user's credentials are verified, the following code attempts to set the RootViewController again back to the original "DashboardViewController" and present that view. This is done from the login controller.
partial void LoginBtn_TouchUpInside (UIButton sender)
{
var appDelegate = UIApplication.SharedApplication.Delegate as AppDelegate;
appDelegate.SetRootViewController (dashboardViewController, true);
this.PresentViewController (dashboardViewController, true, null);
}
Here is the code for the "SetRootViewController" in the AppDelegate:
public void SetRootViewController()
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var storyboard = UIStoryboard.FromName("Main", NSBundle.MainBundle);
bool isLoggedOut = false;
UIViewController rootViewController;
if (isLoggedOut)
rootViewController = (UIViewController)storyboard.InstantiateViewController ("ViewController");
else
rootViewController = (UIViewController)storyboard.InstantiateViewController (viewController);
Window.RootViewController = rootViewController;
Window.MakeKeyAndVisible();
}
This works so far, but if I try to add another view to the stack in the DashboardViewController (which is a UINavigationController), saying from a button click using the following code,
partial void ProfileBtn_TouchUpInside (UIButton sender)
{
this.NavigationController.PushViewController (profileViewController, true);
}
the NavigationController is null and the app crashes. So the basic flow for this scenario is "Set RootViewController to "ViewController" so the user can log in --> When the user logs in set the RootViewController back to the DashboardViewController and present that view --> Click a button to navigate to another view from DashboardViewController view but the NavigationController in DashboardViewController is null.
What am I doing wrong?? Any help or advice would be appreciated (and I apologize for the long question).

I've decided to leave this question up for those that may come upon the same problem. I've figured out what I believe to be a solution. In my AppDelegate "SetRootViewController" method, instead of saying,
Window.RootViewController = rootViewController;
I needed to be saying,
Window.RootViewController = new UINavigationController(rootViewController);
with everything else staying the same. The workflow now works as expected. This is because the controller I am trying to set as the RootViewController is not just a UIViewController, it's a UIViewController wrapped by a UINavigationController. This answer is also reiterated here in the following link
https://forums.xamarin.com/discussion/23424/how-can-i-use-this-navigationcontroller-on-my-view
I apologize for answering my own question, if anybody has a better method or sees something wrong with this please let me know. Hope this helps someone in the future.

Related

Xamarin cache pages - make it so input doesn't disappear

I'm building my first full-scale Xamarin.Forms app and trying to figure out how to keep user input between navigation. After doing some searching online I've read that the default behavior is to completely reload pages each time you navigate, but you can change the default behavior by setting the NavigationCacheMode to true or required, but I've tried to set this attribute in both xaml and C# with no success - it seems like the property is not recognized.
Is there a simple way to make it so that user input does not disappear when navigating between pages? If anyone can show me how to set the NavigationCacheMode that would be great, but I'm also open to any reasonable solution that will keep the user input from disappearing during navigation.
Additional details: My app has a UWP and Android project. I am using a master detail page for navigation. Here is my MenuList_ItemSelected event handler:
private void MenuList_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = (MenuItem)e.SelectedItem;
var title = item.Title;
var page = (Page)Activator.CreateInstance(item.TargetType);
Detail = new NavigationPage(page); //TODO: when menu item is clicked and you're already on that page, the menu should just slide back. (currently it does nothing and stays out).
IsPresented = false;
}
Finally was able to solve this! I adapted this code from a related post which implements a Dictionary that keeps track of the navigation stack:
In the constructor for my Master Detail Page:
public partial class MenuPage : MasterDetailPage
{
Dictionary<Type, Page> menuCache = new Dictionary<Type, Page>();
}
Then in the ItemSelected method:
private void MenuList_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (menuCache.Count == 0)
menuCache.Add(typeof(AttendancePage), Detail);
var item = (MenuItem)e.SelectedItem;
if (item != null)
{
if (menuCache.ContainsKey(item.TargetType))
{ Detail = menuCache[item.TargetType]; }
else
{
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
menuCache.Add(item.TargetType, Detail);
}
menuList.SelectedItem = null; //solves issue with nav drawer not hiding when same item is selected twice
IsPresented = false;
}
}

onCreateView not called on fragment that was inflated by layoutinflator

I have a layout that I want to show as a popup window (used as a custom options menu) while in a fragment of a viewpager. Therefore, when the "Options" button is clicked, I do the following:
public void onOptionsButtonClicked(int buttonHeight)
{
LayoutInflater optionsLayoutInflater = (LayoutInflater)Context.GetSystemService(Context.LayoutInflaterService);
int popupWidth = ViewGroup.LayoutParams.MatchParent;
int popupHeight = ViewGroup.LayoutParams.WrapContent;
View layout = optionsLayoutInflater.Inflate(Resource.Layout.TrackOptions, null);
int popupYOffset = (85 + buttonHeight) * -1;
var popup = new PopupWindow(context);
popup.Focusable = true;
popup.Width = popupWidth;
popup.Height = popupHeight;
popup.ContentView = layout;
popup.SetBackgroundDrawable(new BitmapDrawable());
popup.OutsideTouchable = true;
popup.ShowAsDropDown(view, 0, popupYOffset);
}
And this works as I want, visually that is. Meaning, I click the button and I do see the layout popup as a popup window with all of my options. HOWEVER, none of the buttons work. I put a breakpoint in the class that should be associated the the layout and noticed that onCreateView never gets called, therefore, none of the buttons and associated click event handlers are ever wired up. So, I know why it is not working. However, I don't know how to fix it. I think it is because, while I inflate the view, I am never actually creating the fragment. I have done fragementmanager transactions to replace a fragment in other parts of my project and I know that would probably do it, however, this is a different case as I am trying to do a popup window.
Thanks!
Mike
Fragment is attach in activity so you can try it in onActivityCreated(Bundle) method

Xamarin IOS navigate to another view controller without button on xamarin

i start using xamarin to create ios software.
in mainstoryboard, i navigate PageMain view controller to PageTo view controller.
I want navigate from PageMain view controller to PageTo view controller. i use this code and did not auto navigate:
var storyBoard = UIStoryboard.FromName ("MainStoryboard", null);
storyBoard.InstantiateViewController ("PageTo");
tried this one too but also not auto navigate :
PageMainViewController *viewController = segue. PageToViewController;
viewController.delegate = self;
tried this one too but also not auto navigate :
UIViewController pageto = new PageTo ();
pageto.Transition (PageMain, PageTo);
i know it, it easier use button to create push seque to PageTo view controller, but i did not want it.
please help me.
Another thing that you can do is push to another view controller...
AssignCaseController assignCaseController = this.Storyboard.InstantiateViewController("AssignCaseController") as AssignCaseController;
if (assignCaseController != null)
{
assignCaseController.CaseID = GetCurrentCaseID();
this.NavigationController.PushViewController(assignCaseController, true);
}
I hope this helps, I just had to do the same.
// Clear your notifications and other things
// Show the controller
// I am assuming your are using storyboards based in your use of Handle. I am also assuming the identifier in your storyboard for this is set to "WebViewController and its a custom subclass of UIViewController named WebViewController.
UIStoryboard Storyboard = UIStoryboard.FromName ("MainStoryboard", null); // Assume the name of your file is "MainStoryboard.storyboard"
var webController = Storyboard.InstantiateViewController("WebViewController") as WebViewController;
// ... set any data in webController, etc.
// Make it the root controller for example (the first line adds a paramater to it)
webController.CaseID = int.Parse(notification.UserInfo["ID"].ToString());
this.Window.RootViewController = webController;
// Note* the "Window" should already be set and created because the app is running. No need to remake it.

How do you load a url in a UIWebView

I just started creating a simple application in Xamarin C#.
I have two views : first view have a "login" button and a second view have a webView inside.
I have created the modal segue which connect those two views and my question is :
How can I get if second view is showing it's content and it's webView can load this url : "http://vk.com" ?
Well, at first, you need to add two views.
On the first view place a button. Connect this button to a second view(create a modal segue)
On the second view you can place a label or something different, it doesn't matter.
Zoom out your storyboard(in Xcode storyboard editor, "-" icon), select and set the second view class, for example, "view_two". Save and return to Xamarin Studio. Restart Xcode. Now, in Xamarin Studio select Your class file(in this case - view_two.cs) and open it. Then, insert this code in the "center" of the document :
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
this.webView_login.LoadRequest (new NSUrlRequest (new NSUrl ("http://vk.com")));
}
In the View's ViewDidLoad method:
string url = "http://vk.com";
webView.LoadRequest(new NSUrlRequest(new NSUrl(url)));
See also: http://docs.xamarin.com/recipes/ios/content_controls/web_view/load_a_web_page/
This below piece will will help.
UIWebView webView = new UIWebView(View.Bounds);
View.AddSubview(webView);
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var yourWonFolderPath = Path.Combine(documents, "YourWonFolder");
var localDocUrl = Path.Combine(yourWonFolderPath, TicketInfo.TicketNumber + ".pdf");
webView.LoadRequest(new NSUrlRequest(new NSUrl(localDocUrl, false)));
webView.ScalesPageToFit = true;

MonoTouch Dialog - OnTap Not Working

I am using the Reflection API for MonoTouch.Dialog. What I want to accomplish is, when the user selects an item from a list, I want the navigation controller to go back. I don't want to force the user to click an item, then click the Back button to go back.
However, when trying to use the OnTap attribute, my method doesn't get executed.
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
var demo = new DemoClass();
var context = new BindingContext(this, demo, "Some Demo");
var controller = new DialogViewController(context.Root);
var navController = new UINavigationController(controller);
window.RootViewController = navController;
window.MakeKeyAndVisible ();
return true;
}
public void EnumSelected()
{
InvokeOnMainThread(() =>
{
new UIAlertView("Dialog", "Enum Selected", null, "OK", null).Show();
});
}
DemoClass
public enum DemoEnum
{
SomeValue,
AnotherValue,
YetAnotherValue
}
public class DemoClass
{
[OnTap("EnumSelected")]
public DemoEnum SomeEnum;
}
I know how to navigate back with the navigation controller, but without the OnTap working, I can't get that far. Am I missing something? Can anybody see where I am going wrong?
In a word, you can't.
Enum's (which results in a new RootController and a bunch of RadioElement's) can't have an OnTap set, unless you do it all by hand.
https://github.com/migueldeicaza/MonoTouch.Dialog/blob/master/MonoTouch.Dialog/Reflect.cs#L337
especially, these bits:
csection.Add (new RadioElement (ca != null ? ca.Caption : MakeCaption (fi.Name)));
element = new RootElement (caption, new RadioGroup (null, selected)) { csection };
There is no trigger added to the RadioElement. You would need to change it to auto-pop the form - which needs a new/changed RadioElement
https://gist.github.com/3569920
(I can't claim this code - it came from #escoz: https://github.com/escoz/MonoMobile.Forms )
So, if you are using the built-in MT.D, you can't do it. If you don't mind maintaining your own branch (or, submit a pull request back, which is what I need to do for a few things), then this is a fairly good way to go.

Categories