Pushing a DialogViewController onto a ViewController onto NavigationController stack gives 2 pages - c#

Noob Xamarin/MonoTouch.Dialog question: I have laid out my iOS app in the storyboard designer of Xamarin Studio. I have a UINavigationController with a root view containing a UITableView with static cells, essentially creating a main menu. The cells segue to their respective UIViewControllers.
I want to layout the about/settings screen using MonoTouch.Dialog. However, I am running into some issues combining the overall storyboard approach with the partial MonoTouch.Dialogue approach due to my newbie-ness in Xamarin/MonoTouch.Dialog
When I instantiate a new DialogViewController on the UIViewController that got segued to from the initial UITableView (AccountViewController), I get essentially two views added to the navigation stack, the first only appearing briefly and then showing the DialogViewController. My code instantiating the DialogViewController is this:
partial class AccountViewController : UIViewController
{
public AccountViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var root = new RootElement ("Settings") {
new Section (){
new BooleanElement ("Airplane Mode", false),
new RootElement ("Notifications", 0, 0) {
new Section (null,
"Turn off Notifications to disable Sounds\n" +
"Alerts and Home Screen Badges for the\napplications below."){
new BooleanElement ("Notifications", false)
}
}}
};
var dialog = new DialogViewController (root, true);
this.NavigationController.PushViewController (dialog, true);
//this.NavigationController.PresentViewController (dialog, true, null); tried this but then the UINavigationController shows no back buttons
//View.AddSubview (dialog.View); tried this but then the UINavigationController shows no back buttons
}
}
I'm probably pushing the DialogViewController to late in the stack, creating the intermediary blank screen, but I can't figure out where the right place is to push the DialogViewController into the navigation stack given my current architecture.
Most samples in the interwebs are nearly 100% MonoTouch.Dialog, no storyboard...
Thanks!

You're trying to push another ViewController (in this case a DialogViewController) onto the Navigation Stack while your current ViewController is still trying to load. Another words, doing that in your ViewDidLoad is bad. You're going to have to wait until your current ViewController has finished loading and has gained focus before you can push another view controller onto the stack.
Part of using Storyboards involves the navigation built into it. Am I safe to assume your "AccountViewController" really doesn't do anything? In that case, I would not segue into that. Instead on your previous view controller, just create your DialogViewController, and then manually push it onto the stack. Hard to say without looking at your storyboard and architecture.

I fixed it in the following way:
I created a custom class for my TableViewController (containing a TableView with static cells):
partial class MainMenuTableViewController : UITableViewController
{
public MainMenuTableViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
this.TableView.Delegate = new MainMenuTableViewDelegate (this); //this is the important part. Here I set a custom delegate class for the TableView. I pass the current TableViewController as a parameter in the constructor so I can call the NavigationController from the delegate class to push my custom MonoTouch DialogViewController onto the navigation stack
}
}
Here is the code for the TableViewDelegate:
public class MainMenuTableViewDelegate : UITableViewDelegate
{
private UITableViewController _parentController;
public MainMenuTableViewDelegate(UITableViewController parentController) : base()
{
_parentController = parentController;
}
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
if (indexPath.Row == 2) {
_parentController.NavigationController.PushViewController (new AccountDialogViewController(), true);
}
}
}
I override the RowSelected method in the tableview delegate class and check if the currently selected row equals index 2, the index of the TableViewCell I want to have show my MonoTouch DialogViewController (called AccountDialogViewController). If true, I push a new instance of my AccountDialogViewController onto the navigation stack via the NavigationController property of the parent TableViewController passed in through the constructor.
Here is my AccountDialogViewController:
public class AccountDialogViewController : DialogViewController
{
public AccountDialogViewController () : base(new RootElement("Account settings"), true)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var btnUpdatePassword = UIButton.FromType(UIButtonType.RoundedRect);
btnUpdatePassword.SetTitle("Save new password", UIControlState.Normal);
btnUpdatePassword.Frame = new RectangleF(0, 0, 320, 44);
btnUpdatePassword.TouchUpInside += delegate(object sender, EventArgs e) {
var alert = new UIAlertView("Test", "msg", null, "Cancel", null);
alert.Show();
};
Root.Add(new Section ("General") {
new EntryElement("Username", "type...", "Test"),
new EntryElement("E-mail", "type...", "Test"),
new RootElement ("Change password") {
new Section (null, btnUpdatePassword) {
new EntryElement("New password", null, null, true),
new EntryElement("Confirm", null, null, true)
}
},
new StringElement ("Logout", delegate {
var alert = new UIAlertView("Are you sure?", "Tapping Yes will log you out of your account", null, "Cancel", "Yes");
alert.Show();
})
});
}
}
So, there it is. No more weird blank screens :-)

Related

How to Add a Floating Action Button to Bottom Navigation Bar in IOS (Xamarin)?

How to add a floating action button to bottom navigation bar in IOS (Xamarin)?
Example of screen layout
In order to add navigation bar, using freshmvvm provided function "FreshTabbedNavigationContainer".
However, I have no idea where should I add a floating action button on top of bottom navigation bar with the following code. Please feel free to comment.
using FreshMvvm;
.....
public App()
{
InitializeComponent();
//MainPage = new MainPage();
if (Device.RuntimePlatform == Device.iOS)
{
var tabs = new FreshTabbedNavigationContainer("MyTabs");
tabs.AddTab<MainPageModel>("Home", "");
tabs.AddTab<MainPageModel>("Saved", "");
tabs.AddTab<MainPageModel>("Zoom", "");
tabs.AddTab<MainPageModel>("Notifications", "");
tabs.AddTab<MainPageModel>("Profile", "");
// Set the selected tab to the middle one.
tabs.SwitchSelectedRootPageModel<MainPageModel>();
MainPage = tabs;
}
}
BasePage (Customized base class of contentPage)
public class BasePage : ContentPage
{
public BasePage()
{
NavigationPage.SetHasNavigationBar(this, false);
}
}

Writing to a UITextField in my storyboard but not seeing anything write to it

I am pretty new to IOS development and I am using Xamarin IOS to accomplish this.
The issue I am having is that I have a UITextField declared automatically as an outlet called 'dateInput'. What I am trying to do is write to the text property of this textfield using a string. I am trying to do this inside another method inside my ViewController class. I have instantiated a new instance of the dateInput textfield as a UITextFIeld but when I try to write to this, nothing appears in the textfield.
If I don't create an instance of the dateInput then I just get a Null exception returned. As I said, i'm pretty new so this may just be a simple fix but cannot work it out.Any help would be appreciated.
ViewController Code:
public void HandleDateChange(string dateOutput){
dateInput = new UITextField();
dateInput.Text = dateOutput;
}
And this is the code which is automatically generated for the textfield object:
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UITextField dateInput { get; set; }
ViewDidLoad Method:
public override void ViewDidLoad()
{
base.ViewDidLoad();
testButton.TouchUpInside += (object sender, EventArgs e) =>
{
PerformSegue("dateSegue", this);
};
testLabel.Text = "ViewDidLoad";
}
The point at which the HandleDateChange method is called inside another viewcontroller:
partial void dateValueChanged(UIDatePicker sender)
{
var date = PMSimpilfied.Application.NSDateToDateTime(datePicker.Date);
var dateConversion = date.ToString("dd-MM-yyyy");
NewPmSheetViewController newPmSheetView = new NewPmSheetViewController();
newPmSheetView.HandleDateChange(dateConversion);
}
This is the crash I receive:
[https://i.imgur.com/iFCS69D.png][Crash Log]
Thanks in advance for any help!
Jamie
If you want to pass a parameter to the NewPmSheetViewController, you should not configure the UIViewController's control directly. Because before the UIViewController has been loaded, all of its controls haven't been loaded.
NewPmSheetViewController newPmSheetView = new NewPmSheetViewController(); just means you construct an instance of NewPmSheetViewController. It has not been loaded in the window, so all of its references would be null.
You should create a property in the NewPmSheetViewController, then use this property to show the dateInput's Text:
public string dateOutput { set; get; }
Moreover another error is in your dateValueChanged() when you want to pass the date string. It seems you use UIStoryboard to initialize your UIViewController, the construct method should be like this:
partial void dateValueChanged(UIDatePicker sender)
{
var date = PMSimpilfied.Application.NSDateToDateTime(datePicker.Date);
var dateConversion = date.ToString("dd-MM-yyyy");
// This identifier string can be configured in your Storyboard called "Storyboard ID"
NewPmSheetViewController newPmSheetView = Storyboard.InstantiateViewController("NewPmSheetViewController") as NewPmSheetViewController;
// Pass the parameter through this property
newPmSheetView.dateOutput = dateConversion;
// Try to show the controller on the Window
NavigationController.PushViewController(newPmSheetView, true);
}
At last in the newPmSheetView's ViewDidLoad() event show the string on the UITextField:
public override void ViewDidLoad()
{
base.ViewDidLoad();
if (dateOutput != null)
{
dateInput.Text = dateOutput;
}
}

C#(Xamarin iOS) How to navigate from UITableView to other ViewController?

What line of code should I put inside of RowSelected?
I already used this but no luck at all :/ .
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
while (vc.PresentedViewController != null)
{
vc = vc.PresentedViewController;
}
mainnearmisses myTarget = (UIViewController)vc.Storyboard.InstantiateViewController("mnearm") as mainnearmisses;
myTarget.email = locations[indexPath.Row].shopname + "";
vc.PresentViewController(myTarget, true, null);
}
Some suggestions for you.
1. Set the ViewController itself as parameter in the initializing constructor of the dataSource.
in ViewController
this.TableView.Source = new TableSource(tableItems.ToArray(), this);
in dataSource
ViewController owner;
public TableSource (string[] items, ViewController owner)
{
tableItems = items;
this.owner = owner;
}
this can make your code easy to navigation to another VC.
just use this instead of looking for current vc on Windows .
this.owner.PresentViewController(myTarget, true, null);
2. If your tableView fills with the screen , you can change your ViewController to UITableViewController.
since it inherits form IUITableViewDataSource , so it can handle the event(i.e. that method create the tableView) in its own class.
3.As I mentioned above ,check if that viewController exists
check the storyboard id and find the corresponding viewController in your project.

How to open new TabbedPage from ContentPage?

I am trying to open (using any method) the TabbedPage from ContentPage.
My main App code:
public class App : Application
{
public App ()
{
MainPage = new ConnectPage ();
}
}
My ConnectPage uses XAML, code:
.cs file:
public partial class ConnectPage : ContentPage
{
public ConnectPage ()
{
InitializeComponent ();
}
void connectDevice(object sender, EventArgs args){
connect_btn.Text = "Connecting...";
connect_btn.IsEnabled = false;
var mainapp_page = new MainApp ();
Navigation.PushAsync (mainapp_page);
}
}
XAML file:
<Button x:Name="connect_btn" Text="Connect Now" Clicked="connectDevice"></Button>
Above method throws error:
PushAsync is not supported globally on iOS, please use a
NavigationPage
My MainApp.cs (which contain tabs):
public class MainApp : ContentPage
{
public MainApp ()
{
var tabs = new TabbedPage ();
tabs.Children.Add (new Tab1Page{Title="Tab1" });
tabs.Children.Add (new Tab2Page{Title="Tab2" });
tabs.Children.Add (new Tab3Page{Title="Tab3" });
}
}
You can either update your'r app's MainPage property and set that to the page you want to display, use stack navigation or present a page modally.
Setting a new main page will provide no way for the user to go back:
App.Current.MainPage = new SomeOtherPage ();
If you want to use stack navigation, you will have to wrap your initial page into a NavigationPage:
public partial class App : Application
{
public App ()
{
InitializeComponent ();
this.MainPage = new NavigationPage (new FirstPage ());
}
}
Than you can use Navigation.PushAsync().
If you want to present a page modally, so it is shown on top of your current page and can be dismissed, use:
Navgiation.PushModalAsync(new Page());
However, this will still require to wrap your current page into a NavigationPage.
There are other ways too, like CarouselPage or MasterDetailPage. I recommend you look at the documentation for all of your options.

Xamarin.IOS Add a Splitview to Tabbed Application

I am programming an Application on Xamarin.IOS. I have a Tabbed Layout as for my Main View and all the UIElements are set in the Storyboard. Now, what i need to do, because i want to implement IAds,
i want my Application to be a Split view, with the IAd View Controller as the Detail View, and my usual TabbedLayout as the main view.
So, yeah, my precise question would be:
As all my Layout is loaded from the Storyboard: How can i pass an instance of the View that gets loaded usually to my SplitViewController?
Any tips you can give, are helpful, i really need this for my work!!
To provide some Code:
I am trying to load my initial View (Which is the TabBarController) like this in the AppDelegate.cs:
SplitViewContoller splitView = new SplitViewContoller();
IADViewController iAdVC = new IADViewController (splitView);
Console.WriteLine ("Root" + Window.RootViewController);
Window.RootViewController = iAdVC;
That is working fine, except that the view that usually appears doesn't get loaded... The IAD shows up but the rest of the screen is an empty TabBarController.
Here is my SplitViewController:
public class SplitViewContoller : UISplitViewController
{
UIViewController masterView, detailView;
public SplitViewContoller () : base()
{
// create our master and detail views
masterView = new TabBarController();
detailView = new IADViewController (new TabBarController());
// create an array of controllers from them and then
// assign it to the controllers property
ViewControllers = new UIViewController[]
{ masterView, detailView }; // order is important
}
public override bool ShouldAutorotateToInterfaceOrientation
(UIInterfaceOrientation toInterfaceOrientation)
{
return true;
}
}
The View that i want to be at the bottom of my screen:
(See Monotouch.Dialog and iAds for details)
public partial class IADViewController : UIViewController
{
private UIViewController _anyVC;
private MonoTouch.iAd.ADBannerView _ad;
public IADViewController (UIViewController anyVC)
{
_anyVC = anyVC;
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
View.AddSubview (_anyVC.View);
Version version = new Version (MonoTouch.Constants.Version);
if (version > new Version (6,0))
{
try {
_ad = new MonoTouch.iAd.ADBannerView (MonoTouch.iAd.ADAdType.Banner);
_ad.Hidden = true;
_ad.FailedToReceiveAd += HandleFailedToReceiveAd;
_ad.AdLoaded += HandleAdLoaded;
View.BackgroundColor = UIColor.Clear;
_anyVC.View.Frame = View.Bounds;
View.AddSubview (_ad);
} catch {
}
} else {
Resize ();
}
}
public override void DidRotate (UIInterfaceOrientation fromInterfaceOrientation)
{
base.DidRotate (fromInterfaceOrientation);
Resize ();
}
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
Resize ();
}
void Resize ()
{
UIView.Animate (.25,
() => {
if (_ad !=null && _ad.Hidden == false) {
_anyVC.View.Frame = new RectangleF (0, 0, this.View.Bounds.Width, this.View.Bounds.Height - _ad.Frame.Height);
} else {
_anyVC.View.Frame = View.Bounds;
}
});
if(_ad!=null)
_ad.Frame = new RectangleF (0, _anyVC.View.Bounds.Height, this.View.Bounds.Width, _ad.Frame.Height);
}
void HandleAdLoaded (object sender, EventArgs e)
{
if (_ad == null)
return;
_ad.Hidden = false;
Resize ();
}
void HandleFailedToReceiveAd (object sender, AdErrorEventArgs e)
{
if (_ad == null)
return;
_ad.Hidden = true;
Resize ();
}
}
If anyone else need this, i solved it with the following lines of code now.
Storyboard = UIStoryboard.FromName ("MainStoryboard", null);
initialViewController = Storyboard.InstantiateInitialViewController () as UITabBarController;
// If you have defined a root view controller, set it here:
IADViewController iAdVc = new IADViewController (initialViewController);
Window.RootViewController = iAdVc;
// make the window visible
Window.MakeKeyAndVisible ();

Categories

Resources