MvvmCross v5 animation between two root presentations - ios

I have two iOS views marked by MvxRootPresentation attribute: LoginView without wrapping into navigation controller and MainView with wrapping into navigation controller.
When I call ShowViewModel<MainViewModel>() there is no animation between these two views. All subsequent views are animated as usual (within NavigationController).
How can I set animation for this transition?

Ok, I've did it myself :) I had to add my custom presentation attribute and custom presenter:
public class AnimatedRootPresentationAttribute : MvxRootPresentationAttribute
{
}
public class MyPresenter : MvxIosViewPresenter
{
public MyPresenter(IUIApplicationDelegate appDelegate, UIWindow window)
: base(appDelegate, window)
{
}
protected override void RegisterAttributeTypes()
{
base.RegisterAttributeTypes();
_attributeTypesToShowMethodDictionary.Add(typeof(AnimatedRootPresentationAttribute),
(viewController, attribute, request) => ShowAnimatedRootViewController(
viewController, (AnimatedRootPresentationAttribute)attribute, request));
}
private void ShowAnimatedRootViewController(
UIViewController viewController,
AnimatedRootPresentationAttribute attribute,
MvxViewModelRequest request)
{
ShowRootViewController(viewController, attribute, request);
AddAnimation();
}
private void AddAnimation()
{
var transition = new CATransition
{
Duration = 0.2,
Type = CAAnimation.TransitionMoveIn,
Subtype = CAAnimation.TransitionFromTop
};
_window.RootViewController.View.Layer.AddAnimation(transition, null);
}
}

Related

Mixing a CocosSharp game with UIViewControllers

I'm creating a CocosSharp game with several additional native screens which I'm going to implement using native iOS UIViewController. The flow is the following:
On app startup I'm creating UINavigationViewController and initial view with my custom main menu:
public override void FinishedLaunching(UIApplication app)
{
MainWindow = new UIWindow(UIScreen.MainScreen.Bounds);
var rootViewStoryboard = UIStoryboard.FromName("GameMenu", null);
var rootView = rootViewStoryboard.InstantiateViewController("GameMenu");
var navigationController = new UINavigationController(rootView);
MainWindow.RootViewController = navigationController;
MainWindow.MakeKeyAndVisible();
}
public partial class GameMenu : UIViewController
{
public GameMenu(IntPtr handle)
: base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
btnStartGame.TouchUpInside += (sender, e) => NavigationController.PresentViewController(new GameScene(), false);
}
}
One of the actions leads to another UIViewController (modal) where CocosSharp game is created:
public partial class GameScene : UIViewController
{
public GameScene()
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
_application = new CCApplication();
_application.ApplicationDelegate = new GameAppDelegate();
_application.StartGame();
_application.MainWindow.DisplayStats = true;
OnStopGame();
}
}
Finally on certain event (in my case in 5 sec) I'm trying to finish a game and return back to the main menu:
public async Task OnStopGame()
{
await Task.Delay(5000);
_application.ExitGame();
NavigationController.DismissViewController(false, null);
}
As a result I get empty black screen, background music continue to run, no main menu is displayed.
How should I properly navigate from CocosSharp game to a native controller and back? Can they live in parallel?

MVVMCross iOS: how to bind a command with Custom transition when switching to different View model

For MVVMCross ios, how can I use different TransitionalStyle such as FlipHorizontal style instead of the default sliding effect with "ShowViewModel"?
[Register("SearchResults")]
public class SearchResultsView : MvxTableViewController
{
public override void ViewDidLoad()
{
Title = "List";
base.ViewDidLoad();
var mapButton = new UIButton(new RectangleF(0, 0, 65, 30));
mapButton.SetBackgroundImage(UIImage.FromBundle("images/map_btn.png"), UIControlState.Normal);
mapButton.TouchUpInside += MapButtonClicked();
var rightButton = new UIBarButtonItem(mapButton);
NavigationItem.RightBarButtonItem = rightButton;
var bindings = this.CreateBindingSet<SearchResultsView, SearchResultsViewModel>();
//bindings.Bind(mapButton).To(x => x.ShowMapCommand); //how to do with binding command?
bindings.Apply();
}
private EventHandler MapButtonClicked()
{
return (sender, args) =>
{
var mapView = new SearchResultMapView {ModalTransitionStyle = UIModalTransitionStyle.FlipHorizontal};
var navigationController = new UINavigationController(mapView);
PresentViewController(navigationController, true, null);
};
}
}
ViewModels/Views are presented using a Presenter.
For Modal displays of navigation controllers, some people use the MvxModalNavSupportTouchViewPresenter.cs
You can use this presenter by overriding CreatePresenter in Setup:
protected override IMvxTouchViewPresenter CreatePresenter()
{
return new MvxModalNavSupportTouchViewPresenter(ApplicationDelegate, Window);
}
With this done then you should be able to achieve your transition effect by adding IMvxModalTouchView inheritance and by setting ModalTransitionStyle = UIModalTransitionStyle.FlipHorizontal; in the constructor for your SearchResultMapView.
public class SearchResultMapView
: MvxViewController, IMvxModalTouchView
{
public SearchResultMapView()
{
ModalTransitionStyle = UIModalTransitionStyle.FlipHorizontal;
}
// more code here....
}
Alternatively, if you wanted to completely customise the presentation of the view/viewcontroller, then implementing a custom presenter is fairly straight-forward to do - for more information, see some of the articles and tutorials on custom presenters in http://slodge.blogspot.co.uk/2013/06/presenter-roundup.html
#wing
All you need to do is add a close button to the new view of the pushed view controller and override it's touchupinside as follows:
private void CloseBtnTouchUpInside(object sender, EventArgs eventArgs)
{
DismissModalViewController(false);
}

UIPageViewController Second View Goes Blank

I'm experiencing some very odd behavior when using UIPageViewController in my app. On the first swipe to the next view or anytime the direction of swiping changes (i.e. at the end of the list and going back), the resulting view is blank and I am required to swipe the same direction again for the appropriate view to show.
Example workflow with 3 views:
First view presented
Swipe to the right
Second view blinks in and out
Swipe to the right
Second view presented
Swipe to the right
Third view presented
Swipe to the left
Second view blinks in and out
Swipe to the left
Second view presented
I noticed via logging when the blinking happens (2 & 3 above), the sequence of events is as follows:
GetNextViewController is called
GetPreviousViewController is called
GetNextViewController is called
Then when I swipe again GetNextViewController is appropriately called and the view is shown.
My problem is, obviously, the second view should show without the blinking in and out. I have tried various things but nothing fruitful.
UPDATE
Here is a sample single-controller app that reproduces the problem:
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Collections.Generic;
namespace TestPageView
{
public partial class TestPageViewViewController : UIViewController
{
public TestPageViewViewController () : base ("TestPageViewViewController", null)
{
}
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var controllers = new List<UIViewController>
{
new TestViewController(View, 1),
new TestViewController(View, 2),
new TestViewController(View, 3),
new TestViewController(View, 4)
};
var _pageViewController = new UIPageViewController(UIPageViewControllerTransitionStyle.Scroll, UIPageViewControllerNavigationOrientation.Horizontal);
var dataSource = new ViewDataSource(controllers);
_pageViewController.DataSource = dataSource;
//ViewDataSource has an indexer that returns it's controllers
_pageViewController.SetViewControllers(new[] { controllers[0] }, UIPageViewControllerNavigationDirection.Forward, false, null);
View.AddSubview(_pageViewController.View);
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
// Return true for supported orientations
return (toInterfaceOrientation != UIInterfaceOrientation.PortraitUpsideDown);
}
}
public class TestViewController : UIViewController
{
private readonly UIView _parent;
private readonly int _number;
public TestViewController(UIView parent, int number)
{
_parent = parent;
_number = number;
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var textView = new UITextView(_parent.Bounds);
textView.TextAlignment = UITextAlignment.Center;
textView.AttributedText = new NSAttributedString(_number.ToString(), UIFont.BoldSystemFontOfSize(18));
View.Add(textView);
}
}
public class ViewDataSource : UIPageViewControllerDataSource
{
private LinkedListNode<UIViewController> current;
public ViewDataSource(IEnumerable<UIViewController> controllers)
{
var controllerLiist = new LinkedList<UIViewController>(controllers);
current = controllerLiist.First;
}
public override UIViewController GetPreviousViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
{
if(current.Previous == null){
Console.WriteLine("returning previous nothing");
return null;
}
Console.WriteLine("returning previous something");
current = current.Previous;
return current.Value;
}
public override UIViewController GetNextViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
{
if(current.Next == null){
Console.WriteLine("returning next nothing");
return null;
}
Console.WriteLine("returning next something");
current = current.Next;
return current.Value;
}
}
}
iOS will call GetPreviousViewController and GetNextViewController twice: first for current controller and second for backflip of current controller.
You should do that
current = current.Previous;
only at first call.
Check referenceViewController to do that:
public override UIViewController GetPreviousViewController (UIPageViewController pageViewController, UIViewController referenceViewController)
{
var __c = Pages.Find(referenceViewController);
if (__c.Previous != null)
return __c.Previous.Value;
else
return null;
}
public override UIViewController GetNextViewController (UIPageViewController pageViewController, UIViewController referenceViewController)
{
var __c = Pages.Find(referenceViewController);
if (__c.Next != null)
return __c.Next.Value;
else
return null;
}

MonoTouch Instantiating a ViewController programmatically for ContainerView

I am trying to work with a container view in MonoTouch and I am following some tutorials online. They talk about adding and removing view controller programmatically from the container. I created a viewcontroller and view in the storyboard of my project and attached a few outlets and one action (for labels and buttons respectively). I created an overloaded construc
Here is the code in the view controller that I am trying to add viewControllers into the container view.
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
ContainerView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight;
_controllerOne = new IngredientsController("Perishables");
_controllerTwo = new IngredientsController("Spices");
AddChildViewController(_controllerOne);
ContainerView.AddSubview(_controllerOne.View);
_controllerOne.DidMoveToParentViewController(this)
}
When I add the subview for _controllerOne I get an error because the elements on my controller are marked null. Is MonoTouch incapable of having view controllers being programmatically created if the controller was made in Interface Builder? Below are the two constructors for the Ingredient Controller. When the segue is used then all of the UI controls are initialized properly. Do I need to create the controller programmatically and then instantiate it that way? Any help would be appreciated.
//This ctor does not work
public IngredientsController (string title) : base(NSObjectFlag.Empty)
{
_ingredientTitle = title;
}
//This ctor works
public IngredientsController (IntPtr handle) : base (handle)
{
}
Try to swap the AddSubView() and DidMoveToParentViewController() methods like below:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
ContainerView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight;
_controllerOne = new IngredientsController("Perishables");
_controllerTwo = new IngredientsController("Spices");
this.AddChildViewController(_controllerOne); // Root child controller.
_controllerOne.DidMoveToParentViewController(this); // Confirm the rooting.
ContainerView.AddSubview(_controllerOne.View); // Access the view.
}
Try instantiating the view controller like this:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
this.ContainerView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight;
var newController = this.Storyboard.InstantiateViewController("IngredientsController");
this.AddChildViewController (newController);
this.ContainerView.AddSubview (mapController.View);
}
Make sure you set the Storyboard Id in the properties panel for the ViewController

In MonoTouch, how do I assign a custom view to my view controller?

If I want to use a custom view object with my view controller, instead of just using the one that's initialized by default, can I just assign it to the view controller's View property. For example, is the following the correct/safe approach?
public class MyView : UIView
{
}
public class MyController : UIViewController
{
// Constructors.
public MyController()
{
View = new MyView();
}
}
Seems to work in a simple test, but I don't want to be introducing any time-bombs.
Or, should I be adding my custom view as a subview of the existing view in ViewDidLoad?
You should be adding the custom views as subviews.
public class MyView : UIView
{
}
public class MyController : UIViewController
{
public override void ViewDidLoad()
{
var myView = new MyView();
this.View.AddSubview(myView);
}
}

Resources