My Xamarin IOS project uses a Storyboard. Its a tabbarcontroller app, I'd like to modify the number of tabs in my root UITabBarController.
You can't add or remove tabs if you crate the tabbarcontroller from a Storyboard. I'd like to replace the root view controller with one that is not created from the Storyboard. I'd still like the Storyboard for some of my other classes.
Instructions for creating an empty Xamarin project or removing the Storyboard from an Xamarin project using Visual Studio on the Mac do not work.
I think the new SceneDelegate removes the ability to set a root view controller in AppDelegate.
Thanks,
Gerry
Actually , you don't need to delete the Storyboard . If you want to set the RootViewController in AppDelegate , check the following code.
in Appdelegate
the following code will work before iOS 13.0
public 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
this.Window = new UIWindow(UIScreen.MainScreen.Bounds);
var MainViewController = new MyViewController();
this.Window.RootViewController = MainViewController;
this.Window.MakeKeyAndVisible();
return true;
}
And after iOS 13.0 , we should call the similar code in SceneDelegate , so add the following code to SceneDelegate at same time .
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.Window = new UIWindow(new UIWindowScene(session,connectionOptions));
var MainViewController = new MyViewController();
this.Window.RootViewController = MainViewController;
this.Window.MakeKeyAndVisible();
// This delegate does not imply the connecting scene or session are new (see UIApplicationDelegate `GetConfiguration` instead).
}
In addition , if you want to change the RootViewController in run time (for example when click a button) .
We used Animation to make the process smooth
var MainController = new UITabBarController();
CATransition transition = CATransition.CreateAnimation();
transition.Duration = 0.3;
transition.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseOut);
UIApplication.SharedApplication.KeyWindow.RootViewController = MainController;
UIApplication.SharedApplication.KeyWindow.Layer.AddAnimation(transition, "Animation");
Related
I am creating a page where on click of a button a popover should appear as that in screenshot attached.
I have almost tried many methods to do so.but all ended up showing full screen.Is there really not any way to create a popover in Xamarin iOS?
Can any one help me with this?enter image description here
You can construct the custom pop view controller, then present it with the UIModalPresentationStyle Popover:
//The pop view controller you want to show, here I use Storyboard to initialize it
PopoverViewController popViewController = Storyboard.InstantiateViewController("PopoverViewController") as PopoverViewController;
//Set the presentation style to popover
popViewController.ModalPresentationStyle = UIModalPresentationStyle.Popover;
//The popover view's size
popViewController.PreferredContentSize = new CGSize(150, 150);
//The pop view's position
popViewController.PopoverPresentationController.SourceView = MyBtn;
popViewController.PopoverPresentationController.SourceRect = MyBtn.Bounds;
popViewController.PopoverPresentationController.PermittedArrowDirections = UIPopoverArrowDirection.Up;
//We can also customize the background color
//popViewController.PopoverPresentationController.BackgroundColor = UIColor.White;
popViewController.PopoverPresentationController.Delegate = new PopOverViewDelegate();
PresentModalViewController(popViewController, true);
Do not forget to set the delegate below to achieve the same effect on iPhone:
public class PopOverViewDelegate : UIPopoverPresentationControllerDelegate
{
public override UIModalPresentationStyle GetAdaptivePresentationStyle(UIPresentationController forPresentationController)
{
return UIModalPresentationStyle.None;
}
}
We have a "legacy" Xamarin.Android and Xamarin.iOS project. We would like to inject a Xamarin Forms component as a subview into a "native" Android and iOS view. The component is only available as a Xamarin Forms component and it is too expensive rewrite the entire app to Xamarin.Forms.
The main question is how can we add complex Xamarin.Forms layouts as a subview in a "native" Xamarin.Android or Xamarin.iOS view?
We have done some proof of concept work, and have successfully injected a XF third party component as a single Xamarin Forms element. However, this technique does not fully work when we inject a layout with children elements as eg a StackLayout.
The central pieces of code is e.g in MainActivity.cs and ViewController.cs
MainActivity.cs:
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
Xamarin.Forms.Forms.Init (this, savedInstanceState);
SetContentView (Resource.Layout.Main);
_stackedLabels = new StackedLabels ();
var renderer = Xamarin.Forms.Platform.Android.Platform.CreateRenderer (_stackedLabels.LabelStack); // LabelStack is a StackLayout
var rootLayout = this.FindViewById<LinearLayout> (Resource.Id.root);
rootLayout.AddView (renderer.ViewGroup);
}
ViewController.cs:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
_stackedLabels = new StackedLabels ();
var renderer = Xamarin.Forms.Platform.iOS.Platform.CreateRenderer (_stackedLabels.LabelStack); // LabelStack is a StackLayout
var rootLayout = this.View;
rootLayout.Add (renderer.NativeView);
renderer.NativeView.Frame = this.View.Bounds;
renderer.SetElementSize (new Xamarin.Forms.Size (this.View.Bounds.Width, this.View.Bounds.Height)); // Companion method not found for Android since we do not know exact size in the Android activity OnCreate method.
}
Here, we create a renderer and adds the "native view/view group" manually to the iOS root view present in the view controller or the Android layout which is loaded by the activity.
Expected result would be that the entire StackLayout is displayed with its children. However, none of the children are visible.
We think this has do with how Xamarin.Forms calculates sizes for the VisualElements in the StackLayout, but we have not succeded to trigger
this calculation.
We also note that the iOS renderer sucessfully computes sizes when WidthRequest and HeigthRequest is added to the childs of StackLayout, but not when only HorizontalOptions and VerticalOptions are set.
The main question is how can we add complex layouts as a subview in a "native" Xamarin.Android or Xamarin.iOS view?
I have an app with a medium-sized storyboard, which is complicated enough for me not to want to mess around with it too much.
I want to copy this storyboard and change the color scheme and let the user select which color scheme to use.
My question is: can I programmatically select which storyboard will be used by default on startup? If yes - how do I do that?
I looked at a somewhat related question: Storyboards Orientation Support in Xcode 4.5 and iOS 6.x ?
Based on that code I made an extension method:
static bool IsStoryboardLoading {get;set;}
public static T ConsiderSwitchingStoryboard<T> (this UIViewController from) where T: UIViewController
{
if (!IsStoryboardLoading && LocalStorage.Instance.IsWhiteScheme && false) {
try {
IsStoryboardLoading = true;
UIStoryboard storyboard = UIStoryboard.FromName ("MainStoryboard_WHITE", NSBundle.MainBundle);
T whiteView = storyboard.InstantiateViewController (typeof(T).Name) as T;
from.PresentViewController (whiteView, false, null);
return whiteView;
} finally {
IsStoryboardLoading = false;
}
}
return null;
}
}
and then I use it in ViewDidAppear override:
public override void ViewDidAppear (bool animated)
{
this.ConsiderSwitchingStoryboard<MyViewController> ();
}
This code works in some cases but in others it causes an error when performing a push segue:
NSGenericException Reason: Could not find a navigation controller for segue 'segSearchResults'. Push segues can only be used when the source controller is managed by an instance of UINavigationController.
at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging:void_objc_msgSendSuper_IntPtr_IntPtr (intptr,intptr,intptr,intptr)
It might be simpler to just use 1 Storyboard and have 2 sets of controllers in the same storyboard. Just use different storyboard ids for the controllers. You can use the same class on those if needed.
For example:
var whiteController = Storyboard.InstantiateViewController("MyWhiteController") as MyController;
var blueController = Storyboard.InstantiateViewController("MyBlueController") as MyController;
Both could create an instance of MyController, but pull out different layouts from the same storyboard file.
Another option is to use UIAppearance to dynamically set a "style" on all controls of a certain type in your app.
For example, to set the default UIBarButtonItem image throughout your app:
UIBarButtonItem.Appearance.SetBackgroundImage(UIImage.FromFile("yourpng.png"), UIControlState.Normal, UIBarMetrics.Detault);
(You might check my parameters there)
I have a view in a MonoTouch app using MvvmCross framework that I would like displayed Modal (NavigationController.PresentModalViewController).
MvvmCross starts from the premise that all ViewModels are just "normal pages" - so in iOS/MonoTouch that means UIViewControllers presented using a UINavigationController.
To move away from this premise - towards tabbed displays, modal displays, split controllers, popups, etc - then you can adjust the Presenter logic within your MonoTouch app.
The presenter's job is to implement:
public interface IMvxTouchViewPresenter
{
void Show(MvxShowViewModelRequest view);
void Close(IMvxViewModel viewModel);
void CloseModalViewController();
void ClearBackStack();
bool PresentModalViewController(UIViewController controller, bool animated);
void NativeModalViewControllerDisappearedOnItsOwn();
}
The presenter used for your app is selected in AppDelegate construction - e.g. see how the TwitterSearch builds different presenters for iPhone and iPad.
Fortunately, for simple Modal support, one of the standard presenters available is MvxModalSupportTouchViewPresenter.cs
This presenter looks to see if the view being presented has the IMvxModalTouchView marker interface - it tests view is IMvxModalTouchView. If this interface is present, then it uses modal presentation for the view instead of "normal navigation".
To use this, change your AppDelegate code to something:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
// initialize app for single screen iPhone display
var presenter = new MvxModalSupportTouchViewPresenter(this, window);
var setup = new Setup(this, presenter);
setup.Initialize();
// start the app
var start = this.GetService<IMvxStartNavigation>();
start.Start();
window.MakeKeyAndVisible();
return true;
}
Then add the marker interface to your modal View(s):
public class MyView : MvxBindingTouchViewController<MyViewModel>, IMvxModalTouchView
{
// ....
}
I am new to Monotouch and attempting to understand how some of the basics hang together. Hopefully someone out there will be able to assist.
I've created a test project in MonoDevelop based on the Multi-Screened Apps tutorial on the Xamarin site and have extended it to include a tableView. I am having issues with referencing the Navigation Controller in a view that I need to push a detail view onto to display the detail of an item tapped in the table via an accessory button. I know some of the coding is scrappy, just been trying to get it working at this stage rather than the clarity in the code! (I'm using the latest versions of all Mono tools/libraries etc and XCode 4 on Lion). Starting at the beginning here's the code in FinishedLaunching in AppDelegate.
window = new UIWindow (UIScreen.MainScreen.Bounds);
this.rootNavigationController = new UINavigationController();
// Create a new homescreen
HomeScreen homeScreen = new HomeScreen();
// Add the homescreen to the root navigation controller
this.rootNavigationController.PushViewController(homeScreen, false);
// Set the root view controller on the window - the navigation controller
// will handle the rest.
this.window.RootViewController = this.rootNavigationController;
// make the window visible
this.window.MakeKeyAndVisible ();
homeScreen just contains a button which loads a new view containing a table view (OrderList). Here's the button event handler.
void HandleOrdersButtonhandleTouchUpInside (object sender, EventArgs e)
{
if (orderListScreen == null)
orderListScreen = new OrderList();
NavigationController.PushViewController(orderListScreen, true);
}
This all works fine. I've got some dummy data that loads into the table view, which also works fine. OrderData is a simple class for testing which just contains a couple of properties. I've added an AccessoryButton to the cells and am trying to load a detail view when this is tapped. Here's the code that does this - comment in code where issue is! (I'd previously tested the AccessoryButtonTapped functionilty was working by just displaying an alert).
public override void AccessoryButtonTapped (UITableView tableView, NSIndexPath indexPath)
{
var dataSource = (OrdersTableViewDataSource)tableView.DataSource;
if (detailScreen == null)
detailScreen = new OrderDetailScreen();
OrderData theOrder = dataSource.OrdersData[indexPath.Row];
detailScreen.currentOrder = theOrder;
// Cant get a reference to NavigationController here to push the detail view!
// this.NavigationController is not available
this.NavigationController.PushViewController(detailScreen, true);
}
My understanding of NavigationControllers from what I've read so far is that this reference should be available through all views that originate from the root ViewController/NavigationController without the need to pass the reference from AppDelegate through the various view constructors?
Can anyone tell me what I might be missing here?
Thanks in advance.
** An update after reviewing Jason's comment: (Please let me know if this is the incorrect way to post updates)
So, I tried the following:
I saved a reference to the NavigationController in the constructor for the ViewController that contains the table view as follows:
public partial class OrderList : UIViewController
{
UINavigationController navController;
public OrderList () : base ("OrderList", null)
{
this.Title = "Orders";
navController = this.NavigationController;
}
Then passed that into the TableViewDelegate, where the AccessoryButtonTapped is handled, in the ViewDidLoad method.
public override void ViewDidLoad ()
{
orderTableView.DataSource = new OrdersTableViewDataSource();
orderTableView.Delegate = new OrdersTableViewDelegate(navController);
base.ViewDidLoad ();
}
Then referenced that in the TableViewDelegate:
public class OrdersTableViewDelegate : UITableViewDelegate
{
UINavigationController navController;
public OrdersTableViewDelegate(UINavigationController controller)
{
navController = controller;
}
// Rest of class definition
}
Then the reference to the NavigationController using navController compiles with the code as previously described using the following in the AccessoryButtonTapped method:
navController.PushViewController(detailScreen, true);
When I run this and tap on the AccessoryButton I get a null reference exception on navController. The reference to this.NavigationController in the ViewController constructor is null. Am I doing something in the wrong place or sequence?
Cheers
The NavigationController property is on your table's view controller. If you are trying to reference it from your table's datasource, you need to pass a reference to the controller when you create the datasource.