I have a base UINavigationController class ( xib-less code ) using the singleton pattern with the fallowing code
[MonoTouch.Foundation.Register("NavController")]
public class NavController : UINavigationController
{
public static NavController Instance = new NavController();
public void ChangePage( UIViewController sender, UIViewController page, double duration )
{
this.PopViewControllerAnimated(true);
this.PushViewController( page, false );
//
SetViewControllers( new UIViewController[]{ page }, false );
if( sender != null )
{
sender.View.RemoveFromSuperview();
sender = null;
}
GC.Collect();
}
}
And also i have 4 UIWebController classes with this structure
[Register("HomePage")]
public class AViewPage : UIViewController
{
UIButton btn = new UIButton( new RectangleF( 0,0, 100, 100) );
UIImageView img = new UIImageView( new RectangleF( 100, 100, 200, 200 );
//and a lot of other widgets
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
//
btn.TouchUpInside += delegate{
AnotherViewPage page = new AnotherViewPage();
NavController.Instance.ChangePage( this, page, 1 );
}
//
this.View.AddSubview( btn );
this.View.AddSubview( img );
}
}
My problem is that with current code, memory is leaking. What am I doing wrong?
Should i override the Dispose(bool) and manually dispose everything on the UIWebController classes ?
I also have a lot of UIImages and UIImageViews on this UIViewControllers, what is the proper way of disposing them ?
I have the feeling that the ChangePage function code is ugly. Help me with a better example please.
A fix is coming in the MonoTouch 4.1 beta release, a temporary workaround is this:
Replace this line:
sender.View.RemoveFromSuperview();
With:
var super = sender.View.Superview;
sender.View.RemoveFromSuperview ();
var scratch = super.Subviews;
The correct fix in the next release will just work. Apologies for that.
Ok, stupid question from the beginning.
If you run in to the same problem, just read a little about PushViewController, PopViewControllerAnimated, ViewDidAppear, ViewDidDisappear and ViewDidLoad.
Related
I'm using mvvmcross and implementing the view's interface in code behind. I would like to hide the navigation bar but I have not found a solution yet.
I tried
NavigationController.SetNavigationBarHidden(true, false);
and
NavigationController.NavigationBarHidden = true;
in different methods (ViewDidAppear and ViewWillAppear) but they don't have an impact on the UI.
Maybe someone could give me a hint. :-)
#Edit: Some more information:
My AppDelegate.cs
[Register("AppDelegate")]
public partial class AppDelegate : MvxApplicationDelegate
{
UIWindow _window;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
_window = new UIWindow(UIScreen.MainScreen.Bounds);
var setup = new Setup(this, _window);
setup.Initialize();
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
_window.MakeKeyAndVisible();
return true;
}
}
Additionally I'm using a BaseView class which inherits from MvxViewController.
Okay, found the solution by myself:
Just paste the following code into the ViewDidLoad method in your MvxViewController class( for example FirstView.cs in many mvvmcross tutorials):
var navController = base.NavigationController;
navController.NavigationBarHidden = true;
I know it is a +6 years old question but came across finding a solution for this using MVVMCross and found out that using this into the xaml of your view should be enough: <NavigationPage.HasNavigationBar>False</NavigationPage.HasNavigationBar>
It should apply for both Xamarin Android and iOS.
This will kill it, let me know if you have questions.
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
// class-level declarations
UIWindow window;
MyViewController viewController;
MainViewController mainViewController;
UINavigationController navController;
public UINavigationController NavController { get { return navController; }}
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
var navController = new UINavigationController();
navController.SetNavigationBarHidden (true, false);
window = new UIWindow (UIScreen.MainScreen.Bounds);
viewController = new MyViewController();
app.SetStatusBarStyle (UIStatusBarStyle.LightContent, true);
navController.PushViewController(viewController, false);
window.RootViewController = navController;
window.MakeKeyAndVisible ();
return true;
}
}
}
The default presenter uses a UINavigationController for the RootController on the window; so you can manipulate the navigation bar globally in the AppDelegate by grabbing it off the window and casting:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
new Setup(this, window).Initialize();
Mvx.Resolve<IMvxAppStart>().Start();
var navigationBar = ((UINavigationController)window.RootViewController).NavigationBar;
navigationBar.BackgroundColor = UIColor.Black;
navigationBar.BarTintColor = UIColor.Black;
navigationBar.TintColor = UIColor.White;
window.MakeKeyAndVisible();
return true;
}
EDIT 2: If you're looking for an answer to a similar problem, check Stuart's answer and my comments on it.
EDIT: I am actually getting a Mono.Debugger.Soft.VMDisconnectedException. I also recently installed Windows 8.1 and Resharper (though Resharper is suspended now).
When I access a very simple list property of my view model in my MVVMCross Xamarin iOS application, the program fails. It doesn't quit most of the time: it acts like it's running. The simulator has a black screen and there is no exception. If I breakpoint on if (messagesViewModel != null) source.ItemsSource = messagesViewModel.Messages; and then type messagesViewModel.Messages into the Immediate Window, everything stops, so I can tell it is failing at this line. If instead I "step over", it never moves to the next line.
I was having similar behavior when I was toggling this code in the MvxTableViewSource:
public override int RowsInSection(UITableView tableview, int section)
{
return 1;
}
My view model looks like this:
public class MessagesViewModel : MvxViewModel
{
private List<BaseMessage> _messages = null;
public List<BaseMessage> Messages
{
get
{
return _messages; //yes, I know I'm returning null
//I wasn't at first.
}
}
public MessagesViewModel()
{
}
}
This is my ViewDIdLoad on the MvxTableViewController:
public override void ViewDidLoad()
{
base.ViewDidLoad();
var source = new MessagesTableViewSource(TableView);
//was binding here, removed it for debug purposes
//failure on second line here
var messagesViewModel = ViewModel as MessagesViewModel;
if (messagesViewModel != null) source.ItemsSource = messagesViewModel.Messages;
TableView.Source = source;
TableView.ReloadData();
}
Some initialization code:
public class App : MvxApplication
{
public App()
{
var appStart = new MvxAppStart<MessagesViewModel>();
Mvx.RegisterSingleton<IMvxAppStart>(appStart);
}
}
public partial class AppDelegate : MvxApplicationDelegate
{
//empty functions removed.
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var presenter = new MvxTouchViewPresenter(this, Window);
var setup = new Setup(this, presenter);
setup.Initialize();
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
Window.MakeKeyAndVisible();
return true;
}
}
I suspect whatever the error is, it isn't in any of the code you have posted.
I just created a simple ViewModel:
public class FirstViewModel
: MvxViewModel
{
private List<string> _items = new List<string>() { "One", "Two", "Three"};
public List<string> Items
{
get { return _items; }
set { _items = value; RaisePropertyChanged(() => Items); }
}
}
And a simple View:
[Register("FirstView")]
public class FirstView : MvxTableViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
// ios7 layout
if (RespondsToSelector(new Selector("edgesForExtendedLayout")))
EdgesForExtendedLayout = UIRectEdge.None;
var firstViewModel = ViewModel as FirstViewModel;
var source = new MessagesTableViewSource(TableView);
source.ItemsSource = firstViewModel.Items;
TableView.Source = source;
}
public class MessagesTableViewSource : MvxTableViewSource
{
public MessagesTableViewSource(UITableView tableView) : base(tableView)
{
tableView.RegisterClassForCellReuse(typeof(MessagesCell), new NSString("MessagesCell"));
}
protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item)
{
return tableView.DequeueReusableCell("MessagesCell");
}
}
public class MessagesCell : MvxTableViewCell
{
public MessagesCell(IntPtr handle)
: base(handle)
{
var txt = new UILabel(new RectangleF(0, 0, 320, 44));
Add(txt);
this.DelayBind(() =>
{
this.CreateBinding(txt).Apply();
});
}
}
}
And this code runs fine...
I wouldn't completely trust the integration of Xamarin.iOS with the Immediate window - it is better now than it used to be, but I've seen several problems with it before.
Some things to possibly check:
does the above code work for you?
if it does, then what's in your BaseMessage and MessagesTableViewSource classes - perhaps they are causing the problem?
can you use Mvx.Trace("The list is {0}", messagesViewModel.Messages ?? "-null") to view the list? Can you use trace within the ViewModel property get - is it being called? Can you use trace within the ViewModel constructor?
are all your assemblies building against the same versions of things? Are all your assemblies definitely rebuilt? (Check "Build|Configuration Manager")- what version of Xamarin.iOS are you running in VS and in the Mac?
I added 3 gesture recognizers to my MapView in IB, a long press, a pan & a pinch. Their delegate is the file's owner. I set them up like so -
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
PanGestureRecognizer.AddTarget(s => { Console.WriteLine("Pan"); } );
LongPressGestureRecognizer.AddTarget(s => { Console.WriteLine("Long press"); } );
PinchGestureRecognizer.AddTarget(s => { Console.WriteLine("Pinch"); } );
}
I also implement this -
public bool ShouldRecognizeSimultaneously (UIGestureRecognizer gestureRecognizer, UIGestureRecognizer otherGestureRecognizer)
{
return true;
}
The problem is, only the Long Press gesture recognizer does anything, the others are completely ignored.
Any ideas/suggestions welcome!
Being fairly new to Monotouch, I didn't realise that when I set the delegate of the MapView in IB to my ViewController, that wouldn't actually work. I needed to create a delegate which is a subclass of UIGestureRecognizerDelegate, and set the delegate of the gestureRecognizer to this, and I added the gestureRecognizer programmatically (though that's probably not necessary) -
private class GestureRecognizerDelegate : UIGestureRecognizerDelegate
{
public override bool ShouldRecognizeSimultaneously (UIGestureRecognizer gestureRecognizer, UIGestureRecognizer otherGestureRecognizer)
{
return true;
}
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
UIPinchGestureRecognizer pinchGestureRecognizer = new UIPinchGestureRecognizer(s => { /* do stuff here */ } );
GestureRecognizerDelegate gestureRecognizerDelegate = new GestureRecognizerDelegate();
pinchGestureRecognizer.Delegate = gestureRecognizerDelegate;
MapView.AddGestureRecognizer(pinchGestureRecognizer);
}
Then, by setting the ZoomEnabled property of the MapView to false, I can control how the map zooms (in my case, I had to prevent the map zooming in beyond a certain threshold, my client wasn't happy with the way you could zoom in & it would then bounce back out to my preset value, which I had working using RegionChanged in the MapView delegate). Don't you love clients!
I want to use a DialogViewController inside of a UITabViewController.
Problem: Nested elements don't show a navigation bar, and so it is not possible to go back.
When I push my class (inherited from DialogViewController) to a UINavigationController, then the behavior is correct. If I use the same class in a tab of a UITabViewController (even with an underlying UINavigationController), then the behaviour is wrong.
Can anyone help me out?
Although the question is not assisted with some code sample, I made a small example hoping to solve your question. For this example I used the Tabbed Application template which comes with Xamarin.iOS and named it TabbingTest.
The following code goes in the AppDelegate. Change the FinishedLaunching method to contain:
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
var viewControllers = new UIViewController[]
{
CreateTabFor("Test", "first", new TestDialogController ()),
CreateTabFor("Second", "second", new SecondViewController ()),
};
tabBarController = new UITabBarController ();
tabBarController.ViewControllers = viewControllers;
tabBarController.SelectedViewController = tabBarController.ViewControllers[0];
window.RootViewController = tabBarController;
window.MakeKeyAndVisible ();
return true;
}
Then add the following methods:
private int _createdSoFarCount = 0;
private UIViewController CreateTabFor(string title, string imageName, UIViewController view)
{
var controller = new UINavigationController();
controller.NavigationBar.TintColor = UIColor.Black;
var screen = view;
SetTitleAndTabBarItem(screen, title, imageName);
controller.PushViewController(screen, false);
return controller;
}
private void SetTitleAndTabBarItem(UIViewController screen, string title, string imageName)
{
screen.Title = NSBundle.MainBundle.LocalizedString (title, title);
screen.TabBarItem = new UITabBarItem(title, UIImage.FromBundle(imageName),
_createdSoFarCount);
_createdSoFarCount++;
}
Create a class named TestDialogController and paste the following code inside.
using System;
using MonoTouch.Dialog;
using MonoTouch.UIKit;
namespace TabbingTest
{
public class TestDialogController : DialogViewController
{
public TestDialogController (): base(UITableViewStyle.Plain,null,false)
{
var root = new RootElement ("Tabbing test"){
new Section (){
new RootElement ("First level", 0, 0) {
new Section (null, "This is the first level."){
new RootElement ("Second level", 0, 0) {
new Section (null, "This is the second level."){
new BooleanElement ("Flipflops", false)
}
}
}
}}
};
this.Root = root;
}
}
}
Now run the application.
You can see that even the nested elements show up nicely in the navigation bar. Even with multilevel nesting.
I'm trying to figure out how to have a view. Let's call it ThirdView. It should slide up from the bottom of the screen when a user clicks a particular button on SecondView.
You'll want to create the ThirdView in your SecondView and present it as a modal view, passing in the secondView in the constructor. This will be the easiest way of animating it in the way you would like.
var thirdView = new ThirdView(secondView);
this.PresentModalViewController(thirdView, true);
In your third view, you'll want to call the passed-in SecondView and call:
secondView.DismissModalViewControllerAnimated(true);
Here is a complete working example. It is a tad simpler than in chrisntr's answer...though the above example is what I used to figure everything out.
The coolest thing about this method is that for an artistic custom UI (like the one I am building for a game), there is no off-the-shelf UI elements like the TabBar, Navigation bars, etc. The most creative applications don't use standard UI stuff.
In your main.cs file, in your finishedlaunching block:
ViewController myUIV = new ViewController();
window.AddSubview(myUIV.View);
window.MakeKeyAndVisble();
And then in a new code file add this code:
using System;
using System.Drawing;
using MonoTouch.UIKit;
namespace AnimationTest
{
public class ViewController : UIViewController
{
UIButton uib = new UIButton(new RectangleF(100, 100, 40, 40));
public override void ViewDidLoad()
{
Console.WriteLine("UI1");
this.View.BackgroundColor = UIColor.Blue;
uib.BackgroundColor = UIColor.White;
uib.TouchUpInside += delegate {
Console.WriteLine("Hey!");
var vc2 = new SecondController();
PresentModalViewController(vc2, true);
};
this.View.AddSubview(uib);
base.ViewDidLoad();
}
}
public class SecondController : UIViewController
{
UIButton uib = new UIButton(new RectangleF(100, 100, 40, 40));
public override void ViewDidLoad()
{
this.View.BackgroundColor = UIColor.White;
uib.BackgroundColor = UIColor.Red;
uib.TouchUpInside += delegate {
this.DismissModalViewControllerAnimated(true);
};
this.View.AddSubview(uib);
base.ViewDidLoad();
}
}