I have several modals in my application , here is extentions method what I use for every of them:
public static void CloseAndDismissViewController (this NSViewController vc)
{
vc.View.Window.Close ();
vc.DismissController (vc);
vc.RemoveFromParentViewController ();
}
But I doesn't deallocate memory, here is screen shot from profiler :
finally , such cases cause application crashes....
I'm using PrepareForSegue method to pass data from source to destination vc, like this one :
public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
if (segue.Identifier.Equals ("ImageInfo")) {
var vc = segue.DestinationController as InfoViewController;
vc.SetData (item);
}
}
Related
Please let me re-stress, I am on MvvmCross 6.4.2. I'm currently upgrading a very old project.
I'm getting the error "Trying to show a page without a PageViewController, this is not possible!". According to the source, this is because my PageViewController is null.
I'm not sure why this is, because I've set my pager views and viewmodels up like in the playground sample (the big change in 6.4.2. appears to add attribute support for PageViewControllers).
The PageViewController I want to show (embedded in another ViewController via Container View, for clarity):
[MvxFromStoryboard("Main")]
//[MvxRootPresentation(WrapInNavigationController = true)]
public partial class MyPageViewController : MvxPageViewController<MyPagingViewModel>
And my first page:
[MvxPagePresentation(WrapInNavigationController = false)]
public partial class Page1View : MvxViewController<Page1ViewModel>
{
public Page1View() : base()
{
}
public Page1View(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
UIButton myButton = new UIButton(UIButtonType.System);
myButton.Frame = new CGRect(25, 25, 300, 150);
myButton.SetTitle("Hello, World!", UIControlState.Normal);
}
}
And my pages ViewModel:
public class Page1ViewModel : MvxNavigationViewModel
{
public Page1ViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
{
}
}
I override ShowPageViewController in my custom MvxIosViewPresenter to see what variables are passed in:
protected override Task<bool> ShowPageViewController(UIViewController viewController, MvxPagePresentationAttribute attribute, MvxViewModelRequest request)
The viewController is of type Page1View. Shouldn't it be MyPageViewController though if the method is called ShowPageViewController?
I use a command to navigate to the pages, just like in the playground:
private Task ShowInitialViewModels()
{
var tasks = new List<Task>();
tasks.Add(NavigationService.Navigate<Page1ViewModel>());
return Task.WhenAll(tasks);
}
So what could be going wrong?
I created a View and mapped it to a MKMapView via code, but I cannot seem to have the Navigation Bar show up (it's the 2nd view in the stack, so it should have some ability to show the bar).
The map creates well with all functionality I want, BUT, it takes up the entire view space on the View
public override void LoadView()
{
CoreGraphics.CGRect r = new Rectangle(0, 40, (int)UIScreen.MainScreen.Bounds.Right, (int)UIScreen.MainScreen.Bounds.Bottom);
map = new MKMapView(r);
View = map;
}
any ideas?
To show Navigation Bar , you need make sure that the RootViewController of the Application is an UINavigationController (not an UIViewController) .
in Appdeledate.cs
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
Window = new UIWindow(UIScreen.MainScreen.Bounds);
Window.RootViewController = new UINavigationController(new YourViewController());
Window.MakeKeyAndVisible();
return true;
}
in SceneDelegate (if your app contains it)
public void WillConnect (UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
{
Window = new UIWindow(scene as UIWindowScene);
Window.RootViewController = new UINavigationController(new YourViewController()) ;
Window.MakeKeyAndVisible();
}
And when you navigate from the first page to the map page
NavigationController.PushViewController(new YourMapController(),true);
I am currently trying to center a UIIView inside of a UIScrollView and am having some difficulty in doing so.
Here is the image of my current view:
Here is the code snippet I'm working with:
public void AddView(UIViewController viewCont)
{
this.AddChildViewController(viewCont);
this.mainScrollView.AddSubview(viewCont.View);
viewCont.DidMoveToParentViewController(this);
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
var m = new Menu();
//var c = new Camera();
AddView(m);
AddView(c);
CGRect cFrame = c.View.Frame;
cFrame.X = this.View.Frame.Width;
c.View.Frame = cFrame;
this.mainScrollView.ContentSize = new CGSize(this.View.Frame.Width * 2, 1.0);
}
I want to fill this whole view with Green but as you can see, the bottom quarter of the View does not stretch all the way to the bottom.
For the time being, I have removed all constraints because every attempt in adding them results in no successes. I was hoping to get a concrete answer here as to how I could go about centering this view within this UIScrollView.
Thanks
UPDATE: 3-21-2017
My main goal is to have 2 ViewControllers side by side within my UIScrollView that I can navigate to and from using a swipe gesture, like SnapChat. Following, #Digitalsa1nt suggestions, I unfortunately come up with the same issue.
Here are some more pictures:
This first one shows what happens when I only add the 1 view:
This next one shows what happens when I try to add both views to my UIScrollView, only the camera shows:
Finally, here is the code that I am using to back my Camera view:
using Foundation;
using UIKit;
using AVFoundation;
namespace BRB.iOS
{
public partial class Camera : UIViewController
{
AVCaptureSession captureSession;
AVCaptureStillImageOutput stillImageOutput;
AVCaptureVideoPreviewLayer previewLayer;
public Camera() : base("Camera", null)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
previewLayer.Frame = cameraView.Bounds;
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
captureSession = new AVCaptureSession();
captureSession.SessionPreset = AVCaptureSession.Preset1920x1080;
var backCamera = AVCaptureDevice.GetDefaultDevice(AVMediaType.Video);
NSError error;
var input = AVCaptureDeviceInput.FromDevice(backCamera, out error);
if (error == null && captureSession.CanAddInput(input))
{
captureSession.AddInput(input);
stillImageOutput = new AVCaptureStillImageOutput();
stillImageOutput.OutputSettings = new NSDictionary(AVVideo.CodecKey, AVVideo.CodecJPEG);
if (captureSession.CanAddOutput(stillImageOutput))
{
captureSession.AddOutput(stillImageOutput);
previewLayer = new AVCaptureVideoPreviewLayer(captureSession);
previewLayer.VideoGravity = AVLayerVideoGravity.ResizeAspect;
previewLayer.Connection.VideoOrientation = AVCaptureVideoOrientation.Portrait;
cameraView.Layer.AddSublayer(previewLayer);
captureSession.StartRunning();
}
}
}
}
}
I usually override 'didlayoutsubviews' for making changes to the views within my UIScrollViews. The below worked for me.
public void AddView(UIViewController viewCont)
{
AddChildViewController(viewCont);
mainScrollView.AddSubview(viewCont.View);
viewCont.DidMoveToParentViewController(this);
}
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
// Ensure our contentinsets are 0 so we don't have any blank space
mainScrollView.ContentInset = new UIEdgeInsets(0, 0, 0, 0);
// set the contentsize to the bounds of the container view within.
mainScrollView.ContentSize = View.Bounds.Size;
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
var m = new Menu();
//var c = new Camera();
AddView(m);
AddView(c);
}
What is the correct way to detect when a NSView is resized ?.
I do not see any resize event available on the view or any delegate for the view.
I have added this hack, where I use the drawRect to detect the change in size, but I'm sure there must be a more correct way to do this.
CGRect m_resizeRect = CGRect.Empty;
public override void DrawRect(CGRect dirtyRect)
{
base.DrawRect(dirtyRect);
if (this.InLiveResize) {
if (m_resizeRect.Size != this.Bounds.Size) {
m_resizeRect = this.Bounds;
this.OnResize();
}
}
}
public override void ViewWillStartLiveResize()
{
m_resizeRect = this.Bounds;
base.ViewWillStartLiveResize();
}
public override void ViewDidEndLiveResize()
{
m_resizeRect = CGRect.Empty;
base.ViewDidEndLiveResize();
}
protected void OnResize() {
Console.WriteLine("OnResize " + this.Bounds.ToString() );
}
You can subscribe to the resize notifications.
Add observer to default notification center:
NSObject NSWindowDidResizeNotificationObject;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
NSWindowDidResizeNotificationObject = NSNotificationCenter.DefaultCenter.AddObserver (new NSString ("NSWindowDidResizeNotification"), ResizeObserver, null);
}
NSNotification Action:
public void ResizeObserver (NSNotification notify)
{
var r = this.View.Frame;
Console.WriteLine ("{0}:{1}:{1}", notify.Name, r.Height, r.Width);
}
Remove observer (and release memory):
NSNotificationCenter.DefaultCenter.RemoveObserver (NSWindowDidResizeNotificationObject);
Sample Output:
NSWindowDidResizeNotification:740:740
NSWindowDidResizeNotification:715:715
NSWindowDidResizeNotification:681:681
NSWindowDidResizeNotification:642:642
You can override the setFrameSize method and do your own stuff every time the frame is updated.
class MyView: NSView {
...
override func setFrameSize(newSize: NSSize) {
super.setFrameSize(newSize)
Swift.print("new size is \(frame)")
}
...
}
The accepted answer only seems to respond to window size changes and not e.g. when the split-bar of a splitview causes the resize.
See the NSView postsBoundsChangedNotifications and postFrameChangedNotifications properties. You can set those and register for those notifications.
You can use NSViewController.viewDidLayout() overriden method:
class MyViewController: NSViewController {
...
override func viewDidLayout() {
Swift.print("view has been resize to \(self.view.frame)")
}
...
}
I'm trying to handle the keyDown event for NSTextField using monoMac and Xamarin Studio.
I have created a NSTextFieldDelegate derived class and set the editors delegate to an instance of this class. However, I only see the Changed method being called, never the DoCommandBySelector
class TextPathDelegate : NSTextFieldDelegate {
public override void Changed(NSNotification notification)
{
Console.WriteLine("changed");
}
public override void DidChangeValue(string forKey)
{
Console.WriteLine("didchange = {0}", forKey);
}
public override void WillChangeValue(string forKey)
{
Console.WriteLine("willchange = {0}", forKey);
}
public override bool DoCommandBySelector(NSControl control, NSTextView textView, MonoMac.ObjCRuntime.Selector commandSelector)
{
Console.WriteLine("DoCommandBySelector = {0}", commandSelector.ToString());
return false;
}
}
Any idea what I might be missing ?
Edit-2:
I just noticed that DoCommandBySelector is being called if I use the arrow keys and page up/down. Still not able to catch the keydown event
Edit-1:
I tried to make a small project with Xamarin.mac, just 2 edit fields, with a derived edit control and the delegate.
This is the code
class TextPathDelegate : NSTextFieldDelegate {
string Id { get; set; }
public TextPathDelegate(string id) { Id = id; }
public override void Changed(NSNotification notification)
{
Console.WriteLine(Id + "changed");
}
public override bool DoCommandBySelector(NSControl control, NSTextView textView, ObjCRuntime.Selector commandSelector)
{
Console.WriteLine(Id + "DoCommandBySelector = {0}", commandSelector.ToString());
return false;
}
public override void EditingBegan(NSNotification notification)
{
Console.WriteLine(Id + "EditingBegan");
}
public override void EditingEnded(NSNotification notification)
{
Console.WriteLine(Id + "EditingEnded");
}
}
[Register("MyNSTextField")]
class MyNSTextField : NSTextField {
public MyNSTextField(IntPtr handle) : base(handle){}
public override void KeyDown(NSEvent theEvent)
{
Console.WriteLine("KeyDown");
base.KeyDown(theEvent);
}
public override void KeyUp(NSEvent theEvent)
{
Console.WriteLine("KeyUp");
base.KeyUp(theEvent);
}
}
and the output. No call to keyDown or DoCommandBySelector. I must be missing something very basic
1-EditingBegan
1-changed
KeyUp
1-changed
KeyUp
1-changed
KeyUp
1-EditingEnded
2-EditingBegan
2-changed
KeyUp
2-changed
KeyUp
2-changed
KeyUp
Thanks
Text editing is in Cocoa is really different than WPF ;-) NSTextField and like controls normally do not deal with the actual editing as a NSTextView field editor is used... Strange? Depends upon on how you look at it, for Windows' style controls yes, for Cocoa it is the norm:
Field editor is an NSTextView which is maintained by NSWindow. The field editor replaces any NSTextField or NSTextFieldCell in the window for editing purposes.
Semi-required Reading material : Text Fields, Text Views, and the Field Editor
NSTextField does not respond to a KeyDown as the field editor handles it and all the related functions that it provides (word completion, accent letter popups, etc..., and thus creating a custom NSTextField and overriding KeyDown and KeyUp, only KeyUp will be called:
public override void KeyDown(NSEvent theEvent)
{
throw new Exception("NSTextField KeyDown never gets called");
}
public override void KeyUp(NSEvent theEvent)
{
Console.WriteLine("NSTextField KeyUp");
}
Thus the same goes for NSTextFieldDelegate, you will never get a commandSelector for keydown/keyup... you will get insertNewline:, cancelOperation:, deleteBackward:, etc...
public override void Changed(NSNotification notification)
{
Console.WriteLine("changed"); // after the field editor is done
}
public override bool DoCommandBySelector(NSControl control, NSTextView textView, Selector commandSelector)
{
Console.WriteLine("DoCommandBySelector = {0}", commandSelector.Name);
return false;
}
What to do?
First do you really need keydown? Altering the normal Cocoa UIX is not the norm, but if you really do, one way is to use a custom NSTextView instead of a NSTextField and the KeyDown override will be called:
[Register("CustomTextView")]
public class CustomTextView : NSTextView
{
public CustomTextView() : base() {
Console.WriteLine("CustomTextView");
}
public CustomTextView(IntPtr handle) : base(handle) {
Console.WriteLine("CustomTextView IntPtr");
}
[Export("initWithCoder:")]
public CustomTextView(NSCoder coder) : base(coder) {
Console.WriteLine("CustomTextView initWithCoder");
}
public override void KeyDown(NSEvent theEvent) {
Console.WriteLine("CustomTextView KeyDown");
}
public override void KeyUp(NSEvent theEvent) {
Console.WriteLine("CustomTextView KeyUp");
}
}
If you are using .xib for your interface design, just create a NSTextView subclass in C#, register it and the .h will show up in Xcode and use it to replace the NSTextView with your class.
Depending on what you really need keyDown for, maybe editing begin/end will work for your NSTextField:
this.EditingBegan += (object sender, EventArgs e) => {
Console.WriteLine("EditingBegan");
};
this.EditingEnded += (object sender, EventArgs e) => {
Console.WriteLine("EditingEnded");
};
You can also hook that NSWindow's field editor and listen for all keyDown:, filter the controls and only react if it is a control that you are interested in...