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!
Related
I had an issue in my project and attempted to create a sample project to reproduce it and I was able to.
https://bitbucket.org/theonlylawislove/xamarinnavigationcontrollermemoryleak
The issue is that, when I present a UINavigationController, the navigation controller or its root view controller never gets garbage collected. It DOES work in the iOS simulator though. Why does this memory leak only happen on the device? If you run the sample project on the device, you will never see the Console.WriteLine in the deconstructors called.
I am using XCode5 and Xamarin.iOS 7.0.4.171 (Business Edition)
Here is the AppDelegate I am using to demonstrate the leak.
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
window.RootViewController = new UINavigationController(new RootController ());
window.MakeKeyAndVisible ();
return true;
}
class RootController : UIViewController
{
public RootController ()
{
NavigationItem.RightBarButtonItem = new UIBarButtonItem("Present", UIBarButtonItemStyle.Bordered, (o,e) => {
PresentViewController(new NavigationController(), true, new NSAction(() => {}));
});
}
}
class NavigationController : UINavigationController
{
public NavigationController ()
:base(new TestController())
{
}
~NavigationController()
{
Console.WriteLine("~NavigationController");
}
class TestController : UIViewController
{
~TestController()
{
Console.WriteLine("~TestController");
}
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
Task.Factory.StartNew (() => {
Thread.Sleep(2000);
NSThread.MainThread.InvokeOnMainThread(new NSAction(() => {
DismissViewController(true, new NSAction(() => {
}));
}));
});
}
}
}
}
This is merely a side effect of the conservative collector, there might be some junk on the stack, but using your application will eliminate the junk and allow the object to be released.
If you use SGen, which uses a precise system, you will see the object vanish right away.
I'm searching a way how can i bind ios gesture like UILongPressGestureRecognizer to ICommand or MvxCommand in MvvmCross, thanks.
PS : I found an example here but i can't figure out how to do that.
From the example you found and from the current MVVM Cross source I did the following
public static class MvxBehaviourExtensions
{
public static MvxLongPressGestureRecognizerBehaviour LongPress(this UIView view)
{
var toReturn = new MvxLongPressGestureRecognizerBehaviour(view);
return toReturn;
}
}
and
public class MvxLongPressGestureRecognizerBehaviour
: MvxGestureRecognizerBehavior<UILongPressGestureRecognizer>
{
protected override void HandleGesture(UILongPressGestureRecognizer gesture)
{
// Long press recognizer fires continuously. This will ensure we fire
// the command only once. Fire as soon as gesture is recognized as
// a long press.
if (gesture.State == UIGestureRecognizerState.Began)
{
FireCommand();
}
}
public MvxLongPressGestureRecognizerBehaviour(UIView target)
{
var lp = new UILongPressGestureRecognizer(HandleGesture);
AddGestureRecognizer(target, lp);
}
}
and to bind
set.Bind(this.LongPress()).For(lp => lp.Command).To(c => c.DoTheStuffCommand);
I have an iPhone app that supports all orientations but in one UIViewController I only want to support portrait (or upside down).
I have added the following code to my UIViewController but it still rotates.
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
// Return true for supported orientations
return ((toInterfaceOrientation != UIInterfaceOrientation.LandscapeLeft) && (toInterfaceOrientation != UIInterfaceOrientation.LandscapeRight));
}
No matter where I add the ShouldAutorotateToInterfaceOrientation code it still rotates!
Is there a way to allow the app to support all orientations for all UIViewControllers except one?
Also i am using a NavigationController - does that affect things?
if you use iOS 6 you must override GetSupportedInterfaceOrientations method instead ShouldAutorotateToInterfaceOrientation
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations()
{ ... }
Probably this happens because your root navigation controller brings it own rotation. Lets assume that your root navigation controller is a normal UINavigationViewController. Create a derived own version:
public class UINavigationControllerWithoutRotation : UINavigationController
{
public UINavigationControllerWithoutRotation()
{
}
public UINavigationControllerWithoutRotation(UIViewController viewController) : base(viewController)
{
}
public override bool ShouldAutorotate()
{
return false;
}
}
Now use this as the root controller. Find the code saying something like:
var myRootController = new UINavigationController();
and replace it with
var myRootController = new UINavigationControllerWithoutRotation();
That should do the job. Keep in mind, that you have to do it a little bit different if for instance your root view controller is a UITabBarController!
I have a custom button which inherits from UIButton. I'm handling the TouchUpInside event and want to display a view on top of the current View. Is there such a thing as Dialogs like in Windows development? Or should I do this in another way?
[MonoTouch.Foundation.Register("HRPicker")]
public class HRPicker : UIButton
{
public HRPicker () : base()
{
SetUp();
}
public HRPicker(NSCoder coder) : base(coder)
{
SetUp();
}
public HRPicker(NSObjectFlag t) : base(t)
{
SetUp();
}
public HRPicker(IntPtr handle) : base(handle)
{
SetUp();
}
public HRPicker(RectangleF frame) : base(frame)
{
SetUp();
}
public void SetUp()
{
TouchUpInside += HandleTouchUpInside;
}
void HandleTouchUpInside (object sender, EventArgs e)
{
//I want to display a View here on top of the current one.
}
}
Thanks,
Yes, you have a couple options:
ModalViewController - is called from any UIViewController and overlays a ViewController in the foreground.
UIPopoverController - is a native control that takes a UIViewController and has hooks for presentation and dismissal
WEPopoverController - is a re-implementation of UIPopoverController and allows you to customize the layout, size, and color of the Popover container.
ModalViewController: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
UIPopoverController: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIPopoverController_class/Reference/Reference.html
WEPopoverController: https://github.com/mono/monotouch-bindings/tree/master/WEPopover
Update: Regardless of which option you use you must call the presentation of the Popover / Modal view from the main thread:
using(var pool = new NSAutoReleasePool()) {
pool.BeginInvokeOnMainThread(()=>{
// Run your awesome code on the
// main thread here, dawg.
});
}
The equivalent of dialog in Cocoa is UIAlertView: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAlertView_Class/UIAlertView/UIAlertView.html
Check out this question for an example of how to use it: Showing an alert with Cocoa
The code should be pretty easy to translate to c# and MonoTouch. But here is a simple example: http://monotouchexamples.com/#19
I have QLPreviewController up and running but I'm using PresentModalViewController() to show the QLPreviewController directly. For reasons beyond explanation, I would like to have my own UIViewController which will create its own view and within that view I would like to use the QLPreviewController. Should be easy I thought, but the code below just does nothing. The QLPreviewControllers ViewDidAppear never gets called. (In my example below, PreviewController inherits from QLPreviewController and encapsulates delegate, preview item and source).
Can somebody explain what is wrong with the code below (besides the fact that it is pointless :-))?
Oh, yeah: in my test scenario, I present the controller below modally. It shows up but witout the preview.
public class OuterPreviewController : UIViewController
{
public OuterPreviewController (QLPreviewControllerDataSource oDataSource) : base()
{
this.oDataSource = oDataSource;
}
private PreviewController oPreviewController;
private QLPreviewControllerDataSource oDataSource;
public override void LoadView ()
{
this.View = new UIView();
this.View.Frame = new RectangleF(0, 0, 500, 500);
this.View.BackgroundColor = UIColor.Red;
}
public override void ViewDidAppear (bool animated)
{
// Code execution comes her. No errors, no issues.
base.ViewDidAppear (animated);
this.oPreviewController = new PreviewController();
this.oPreviewController.DataSource = this.oDataSource;
// Preview controller's view is added but it never shows up.
this.View.AddSubview(this.oPreviewController.View);
this.oPreviewController.View.Frame = this.View.Frame;
this.oPreviewController.View.Center = this.View.Center;
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
return true;
}
}
Found a solution by coincidence today: all ReloadData() on the preview controller and magically it will show its contents.
This allows to add a QLPreviewController to an existing view as a subview and embed a preview. It also gets you rid of the toolbar which contains the open in menu.