I'm trying to get a basic flip animation transition working when I push a controller inside a navigation. The code below flips the view, however the view appears first (each element fades in), and then the flip occurs. Is it possible to do a flip animation with a UINavigationController?
Any pointers would be great, the examples I've found for Monotouch are performing animations on Views inside another view.
void ToolbarButtonClick()
{
InformationController controller = new InformationController();
NavigationController.PushViewController(controller,true);
}
public class InformationController : UIViewController
{
public override void ViewDidLoad ()
{
UIView.BeginAnimations("Flip");
UIView.SetAnimationDuration(1.0);
UIView.SetAnimationTransition(UIViewAnimationTransition.FlipFromRight,View,true);
base.ViewDidLoad ();
Title = "Information";
}
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
}
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
UIView.CommitAnimations();
}
}
I was sort of there, but the View needs to be taken from the NavigationController:
// Push the controller first or the Title doesn't animate
NavigationController.PushViewController(controller,false);
UIView.BeginAnimations(null,IntPtr.Zero);
UIView.SetAnimationDuration(1);
UIView.SetAnimationTransition(UIViewAnimationTransition.FlipFromLeft,
NavigationController.View,true);
UIView.CommitAnimations();
I'm not an expert, but I'm wondering about that TRUE on the PushViewcontroller which indicates that it is going to be animated. It has me wondering if that is making the NavigationController do the initial amount of animation work, which is then followed by yours. When you set it to false, what happens? I know we tend to automatically put the TRUE in there without thinking.
Related
I am registering for Notifications in ViewDidLoad() in my Xamarin project. However I don't see any method where I can "Remove" the Observer. I need to execute something even when the View is not visible, so I can't give it in ViewWillAppear. Is there anything similar to "deinit" method.
Tried out "dispose" method in C#, but it didn't get called.
Even after doing a "PopViewController" the observer is still not getting unregistered. Not sure why it is not getting released from the memory even when the view controller is removed.
Depending on your needs you could override ViewWillAppear and/or ViewDidDisappear to add/remove it there.
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
// subscribe
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(animated);
// unsubscribe
}
Keep in mind ViewDidDisappear will be called when a new controller is pushed on top of your current controller and ViewWillAppear will get called again when that new controller is popped, which might not be suitable for whatever you're trying to do.
You can do all subscription for events in ViewDidLoad and unsubscribing them would happen automatically from IOs. If you really feel to unsubscribe them, then do it in Dispose method.
Here's an example,
In the ViewController,
// Subscribing
public override void ViewDidLoad()
{
_homeView.HomeButton.TouchUpInside += OnHomeButtonClicked;
}
// Unsubscribing
protected override void Dispose(bool disposing)
{
_secondView.SecondButton.TouchUpInside -= OnSecondButtonClicked;
base.Dispose(disposing);
}
So currently the user is in ViewController2, to transition to ViewController1 the presentViewController is being called.
Soon after ViewController1 opens up, there is an IBAction method that needs to be called.
How can this be accomplished? Any help would be greatly appreciated. Thank you!
The view controller can call it itself in either viewWillAppear or viewDidAppear.
self.myAction()
If the view controller is exclusively used in the context described you could do it unconditionally, otherwise if you need to do it conditionally - expose a Boolean:
public var doActionAfterAppear = false
public override viewDidAppear(animated isAnimated: Bool) {
super.viewDidAppear(animated: animated)
if self.doActionAfterAppear {
self.myAction()
self.doActionAfterAppear = false
}
}
And lastly before you present the second view controller or transition to it:
nextViewController. doActionAfterAppear = true
The best method in iOS to subscribe a event is ViewDidLoad, but when dismiss the view , the ViewDidUnload() is not called(only when the memory warning.)
Which place is the best to unsubscribe the event?
(In the subviewController I subscribe a event that reference the MainViewController, When open the subview twice, I receive two event trigger because the unsubscribe in viewdidunload() is never called.)
How about with subscribe/unsubscribe in ViewWillAppear/ViewWillDisapper?
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
this.mBL.OrderChanged += HandleOrderChanged;
}
public override void ViewWillDisappear (bool animated)
{
base.VieWillDisappear (animated);
if (this.mBL!=null)
this.mBL.OrderChanged -= HandleOrderChanged;
}
Use ViewDidLoad and ViewDidUnload, those are the appropriate places to subscribe/unsubscribe events from the UI.
Here is a general article on memory management in iOS that I think applies here: http://www.buildingiphoneapps.com/buildingiphoneapps/2010/6/25/memory-management-and-viewdidunload.html
Now, if you're not wanting to have the event run when your View is not visible, do something like this in the event handler:
if (IsViewLoaded && View.Window != null) {
//code here
}
I've found this is the easiest way to tell if the view is on screen.
I agree, the best time is in ViewWillAppear/ViewWillDisappear, not ViewDidUnload.
ViewDidUnload is called called post iOS 6:
https://developer.xamarin.com/api/member/UIKit.UIViewController.ViewDidUnload()/
I would like to wait for a controller being displayed with PresentModalViewController() to finish its job before resuming execution (like modal dialogs in WinForms). How can this be done with monotouch?
I know there is a similar question on SO but the answer is for Objective-C and, frankly, I don't get it.
Many thanks.
EDIT
Here's the first setup I tried and which didn't seem to work:
Create a new Project (Single View Application);
Add two controllers (iPhone View Controller): FirstController and SecondController; the first controller overrides DismissModalViewControllerAnimated and fires a OnFirstFinished event just after being dismissed;
In the main controller:
...
public partial class TestModalViewController : UIViewController
{
private UIButton button;
private FirstController first;
private SecondController second;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
button = UIButton.FromType(UIButtonType.RoundedRect);
button.Frame = new RectangleF(0, 0, 100, 50);
button.SetTitle("Test", UIControlState.Normal);
button.TouchUpInside += PresentFirstController;
View.Add(button);
PresentFirstController(null, null);
}
void PresentFirstController (object sender, EventArgs e)
{
bool firstFinished = false;
first = new FirstController();
first.OnFirstFinished += delegate(object s, EventArgs args) {
firstFinished = true;
};
this.PresentModalViewController(first, true);
do
{
NSRunLoop.Current.RunUntil (NSDate.FromTimeIntervalSinceNow (0.5));
} while (!firstFinished);
second = new SecondController();
this.PresentModalViewController(second, true);
}
// ...
}
In FirstController:
...
public override void DismissModalViewControllerAnimated (bool animated)
{
base.DismissModalViewControllerAnimated (animated);
if(null != OnFirstFinished)
{
OnFirstFinished(this, null);
}
}
In this setup the execution blocks (black loading screen) and the first controller isn't loaded.
If the call to PresentFirstController() is removed from ViewDidLoad(), the main controller loads fine and when clicking the "Test" button the first controller is loaded. However, after the first controller is dismissed, the second controller is NOT loaded - iOS doesn't seem to like presenting a modal controller right after dismissing another one. This can be solved by adding a small delay (but how small is still safe?) like below:
public override void DismissModalViewControllerAnimated (bool animated)
{
base.DismissModalViewControllerAnimated (animated);
NSRunLoop.Current.RunUntil (NSDate.FromTimeIntervalSinceNow(0.2));
if(null != OnFirstFinished)
{
OnFirstFinished(this, null);
}
}
In general, the code pattern that you want to use on iOS is to chain these actions. For example, your view controller would likely have a login/password field, and a button to do the login.
What you would do is connect an action to the Login button that contacts the server, validates the user and if the credentials are OK, dismiss the dialog view controller and at that point resume execution.
That said, you could present the view controller and run the UI main loop manually and wait for some event to trigger before resuming execution.
do {
NSRunLoop.Current.RunUntil (NSDate.FromTimeIntervalSinceNow (0.5));
} while (!done);
You can save yourself some pain and structure your code with the UIKit patterns instead of trying to fight them. You will end up saving time.
As title said, I want to show another UIViewController from an existing UIViewController which is hosted in UIPopoverController. I tried the following method:
_secondViewController = new SecondViewController();
this.ModalPresentationStyle = UIModelPresentationStyle.CurrentContext;
this.ModelInPopover = true;
this.PresentModelViewController(_secondViewController, true);
However, the secondViewController is shown in the main view controller, instead of the popover controller.
In this post somebody mentions that it cannot be done and it violates the HIG. However, I have seen this in other apps (e.g. Yahoo! Email) if I'm not mistaken.
I'm also thinking about another approach: If I could create a UINavigationController within the popover context, it might work by just adding new ViewController to the NavigationController. But how?
Remember that UINavigationController derives from UIViewController.
So, you can use the controller contained within UIPopover just like any other container... in this case it's best to use UINavigationController inside UIPopover to display ViewControllers.
Usage:
var _NavController = new NavController();
Popover = new UIPopoverController(_NavController);
Popover.PopoverContentSize = new SizeF(..., ...);
Popover.PresentFromRect(...);
NavController:
public class NavController : UINavigationController
{
UIViewController _FirstViewController;
UIViewController _SecondViewController;
public NavController()
: base()
{
}
public override void LoadView()
{
base.LoadView();
_FirstViewController = new UIViewController();
// Initialize your originating View Controller here.
// Only view related init goes here, do everything else in ViewDidLoad()
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// When a button inside the first ViewController is clicked
// The Second ViewController is shown in the stack.
_FirstViewController.NavButton.TouchUpInside += delegate {
PushSecondViewController();
};
this.PushViewController(_FirstViewController, true);
}
public void PushSecondViewController()
{
_SecondViewController = new UIViewController();
this.PushViewController(_SecondViewController, true);
}
}