How to handle UITextField position while typing in Xamarin iOS? - xamarin

Its very common problem in iOS mobile development and that is while you are done with your UI and It contains too many UITextFields, If you try to input value in UITextFields those are added center bottom of the screen; these fields hides behind the keyboard. How can we get rid of this general problem?

You could use the AddObserver method in NSNotificationCenter for when keyboard is visible and hidden.
sample code(FYI: Got the code below from another post sometime last year, I can't remember link to the post but it works fine.)
Call the AddObserver in your viewdidload method
// Keyboard popup NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.DidShowNotification, KeyBoardUpNotification);
// Keyboard Down NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, KeyBoardDownNotification);
you can add the methods below in your base controller base if you have one
public void KeyBoardUpNotification(NSNotification notification) {
CGRect keyboardSize = UIKeyboard.BoundsFromNotification(notification);
// Find what opened the keyboard
foreach (UIView view in this.View.Subviews) {
if (view.IsFirstResponder)
activeview = view;
}
bottom = (activeview.Frame.Y + activeview.Frame.Height + offset);
scrollamount = (keyboardSize.Height - (View.Frame.Size.Height - bottom));
if (scrollamount > 0) {
moveViewUp = true;
MoveView(moveViewUp);
} else {
moveViewUp = false;
}
}
public void KeyBoardDownNotification(NSNotification notification) {
if (moveViewUp) {
MoveView(false);
}
}
private void MoveView(bool move) {
UIView.BeginAnimations(string.Empty, IntPtr.Zero);
UIView.SetAnimationDuration(0.3);
CGRect frame = View.Frame;
if (move) {
frame.Y -= scrollamount;
} else {
frame.Y += scrollamount;
scrollamount = 0;
}
View.Frame = frame;
UIView.CommitAnimations();
}

I have used a nuget package to get rid of this problem. I have overrides two methods and initialized code inside these methods.
Download KeyboardHandler and use as following:
using KeyboardHandler;
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
this.yourScrollView.SubscribeKeyboardManaqger();
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
this.yourScrollView.UnsubscribeKeyboardManaqger();
}

Related

Centering a UIView within a UIScrollView Xamarin.iOS

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);
}

How to detect resize in NSView in Xamarin.Mac?

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)")
}
...
}

Xamarin forms block user back key press

In my Xamarin forms application I want to show a confirmation message when user clicks the back button from Main-page. Is there any way to achieve this?
I overrided the OnBackButtonPressed method in my MainPage. But still the app is closing while back key press. Here is my code
protected override bool OnBackButtonPressed ()
{
//return base.OnBackButtonPressed ();
return false;
}
You can override OnBackButtonPressed for any Xamarin.Form Page. But it only will work for the physical button in Android and Windows Phone devices.
protected override bool OnBackButtonPressed () {
DisplayAlert("title","message","ok");
return true;
}
For the virtual one, you will need to create CustomRenderers and to intercept the click handler. In iOS it can be tricky because the user can go back doing other actions (e.g. the swipe gesture). Once you intercept it you just need to create your Confirmation Message (which I assume that you know how to do it).
For iOS you can do something like this:
[assembly: ExportRenderer (typeof (YourPage), typeof (YourPageRenderer))]
namespace YourNamespace {
public class YourPageRenderer : PageRenderer {
public override void ViewWillAppear (bool animated) {
base.ViewWillAppear (animated);
Action goBack = () => page.DisplayAlert("title","message","ok");
var backButton = new NavBackButton (goBack);
navigationItem.LeftBarButtonItem = new UIBarButtonItem (backButton);
}
}
public class NavBackButton : UIView {
public NavBackButton (Action onButtonPressed) {
SetButton (onButtonPressed);
}
UILabel text;
UIImageView arrow;
void SetButton(Action onButtonPressed){
arrow = new UIImageView(new CGRect(-25,0, 50, 50)) {
Image = new UIImage("Images/back").ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
};
arrow.TintColor = Colors.DarkGreen.ToUIColor ();
text = new UILabel(new CGRect(arrow.Frame.Width + arrow.Frame.X -15, arrow.Frame.Height /2 - 10, 40, 20)) { Text = "Back" };
text.TextColor = Colors.DarkGreen.ToUIColor ();
Frame = new CGRect(0,0,text.Frame.Size.Width + arrow.Frame.Width, arrow.Frame.Height);
AddSubviews (new UIView[] { arrow, text });
var tapGesture = new UITapGestureRecognizer (onButtonPressed);
AddGestureRecognizer (tapGesture);
}
public override void TouchesBegan (Foundation.NSSet touches, UIEvent evt) {
base.TouchesBegan (touches, evt);
text.TextColor = UIColor.YourColor;
arrow.TintColor = UIColor.YourColor;
}
public override void TouchesEnded (Foundation.NSSet touches, UIEvent evt){
base.TouchesEnded (touches, evt);
arrow.TintColor = UIColor.YourColor;
text.TextColor = UIColor.YourColor;
}
}
}
PS You will need to provide an arrow image ("Images/back")

How can I add a Dialog View Controller as a Subview to a UIView or Vice Versa?

I have looked around the web for some time looking for any resources on this topic and have come up with nothing that solves my dilemma.
I have a dialog view controller and its root is simply displaying a list of strings similar to how the iphone music song scrollable view is laid out. What I need is a subview located at the top of the screen and the scrollable DVC below it. I need to the top view to be always in view while the user can scroll through the root element because the top view will be holding statistics.
I have tried adding a subview but it simply overlaps the dvc below it, and I have not been able to figure out a way to add a dvc as a subview to a UIView.
Any help would be much appreciated.
What is needed to achieve this is a single root view controller that hosts two subview controllers. One subview contains the statistics at the top of the window. The bottom subview contains a navigation controller that holds the dialog view.
using System;
using System.Collections.Generic;
using System.Linq;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.Dialog;
using System.Drawing;
namespace delete201205203
{
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
MyUIViewController _mvc;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
_mvc = new MyUIViewController ();
window.RootViewController = _mvc;
window.MakeKeyAndVisible ();
return true;
}
}
public class MyUIViewController : UIViewController
{
MyDialogViewController _dvc;
UINavigationController _nav;
StatisticsViewController _statistics;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var root = new RootElement ("Root") {
new Section ("Section") {
new EntryElement ("caption", "placeholder", ""),
new RootElement ("Root 2") {
new Section ("Section") {
new EntryElement ("caption", "placeholder", ""),
new StringElement ("Back", () => {
_nav.PopViewControllerAnimated (true);
})
}
}
}
};
_dvc = new MyDialogViewController (root);
_nav = new UINavigationController (_dvc);
_nav.SetNavigationBarHidden (true, false);
_nav.View.Frame = new RectangleF (0, 70f,
this.View.Bounds.Width,
this.View.Bounds.Height -70f);
_statistics = new StatisticsViewController ();
_statistics.View.Frame = new RectangleF (0, 0,
this.View.Bounds.Width,
70f);
this.AddChildViewController (_nav);
this.View.AddSubview (_nav.View);
this.AddChildViewController (_statistics);
this.View.AddSubview (_statistics.View);
}
public override void ViewWillLayoutSubviews ()
{
base.ViewWillLayoutSubviews ();
_nav.View.Frame = new RectangleF (0, 70f,
this.View.Bounds.Width,
this.View.Bounds.Height -70f);
_statistics.View.Frame = new RectangleF (0, 0,
this.View.Bounds.Width,
70f);
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
return true;
}
}
public class StatisticsViewController : UIViewController
{
UILabel _label;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
this.View.BackgroundColor = UIColor.White;
_label = new UILabel (new RectangleF (this.View.Bounds.Width * .5f - 50f,
this.View.Bounds.Height * .5f -10f,
100f, 20f));
_label.AutoresizingMask = UIViewAutoresizing.FlexibleMargins;
_label.Text = "statistics";
this.View.AddSubview (_label);
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
return true;
}
}
// This overrde is needed to ensure the pop view animation
// works correctly in landscape mode
public class MyDialogViewController : DialogViewController
{
public MyDialogViewController (RootElement root) : base (root) {}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
return true;
}
}
}

Slowly initialized components that cause lack of user experience

This is the view that appears when I click a button on a previous view.
The text boxes, the smiling face image and the labels are predesigned created by xCode.
Please see the image and the code of the view to clear why all the view's components are initializing very slowly and getting ready to give the last shoot that is captured by me when it is finished to be totally loaded . Moreover, It is very slow when I type letters, the letters are appearing very slowly while I am typing with the keyboard that iOS provides on every touch on the text box.
The Code of The View;
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace IstanbulCity
{
public partial class AskForNAme : UIViewController
{
public delegate void AskForNAmeClosingDelegate (AskForNAme form);
public event AskForNAmeClosingDelegate AskForNAmeClosed;
NSObject obs1;
float scrollamount = 0.0f;
float bottomPoint = 0.0f;
float yOffset = 0.2f;
bool moveViewUp = false;
public AskForNAme () : base ("AskForNAme", 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 ();
// Perform any additional setup after loading the view, typically from a nib.
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(true);
obs1 = NSNotificationCenter.DefaultCenter.AddObserver (
"UIKeyboardDidShowNotification", KeyboardUpNotification);
this.tbOwnerMailAdress.ShouldReturn += TextFieldShouldReturn;
this.tbOwnerBirthDay.ShouldReturn += TextFieldShouldReturn;
this.uivGuguPhoto.Image = UIImage.FromFile ("image/fcuk.jpeg");
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(false);
obs1 = NSNotificationCenter.DefaultCenter.AddObserver (
"UIKeyboardDidShowNotification", KeyboardUpNotification);
this.tbOwnerMailAdress.ShouldReturn += TextFieldShouldReturn;
this.tbOwnerBirthDay.ShouldReturn += TextFieldShouldReturn;
this.uivGuguPhoto.Image = UIImage.FromFile ("image/fcuk.jpeg");
}
public override void ViewDidUnload ()
{
base.ViewDidUnload ();
// Clear any references to subviews of the main view in order to
// allow the Garbage Collector to collect them sooner.
//
// e.g. myOutlet.Dispose (); myOutlet = null;
ReleaseDesignerOutlets ();
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
// Return true for supported orientations
return (toInterfaceOrientation != UIInterfaceOrientation.PortraitUpsideDown);
}
void HandleIstanbulCityViewControllerClosed (babyAge form)
{
form.DismissModalViewControllerAnimated (true);
form = null;
}
partial void tbKadikoyHallEditDidEndOnExit (MonoTouch.Foundation.NSObject sender)
{
tbIstanbulName.ResignFirstResponder ();
}
private bool TextFieldShouldReturn (UITextField tf)
{
tf.ResignFirstResponder ();
if (moveViewUp) {
ScrollTheView (false);
}
return true;
}
private void KeyboardUpNotification (NSNotification notification)
{
ResetTheView ();
RectangleF r = UIKeyboard.BoundsFromNotification (notification);
if (this.tbOwnerMailAdress.IsEditing ) {
//Calculate the bottom of the Texbox
//plus a small margin...
bottomPoint = (this.tbOwnerMailAdress.Frame.Y + this.tbOwnerMailAdress.Frame.Height + yOffset);
//Calculate the amount to scroll the view
//upwards so the Textbox becomes visible...
//This is the height of the Keyboard -
//(the height of the display - the bottom
//of the Texbox)...
scrollamount = (r.Height - (View.Frame.Size.Height - bottomPoint));
}
else if (this.tbOwnerBirthDay.IsEditing)
{
bottomPoint = (this.tbOwnerBirthDay.Frame.Y + this.tbOwnerBirthDay.Frame.Height + yOffset);
scrollamount = (r.Height - (View.Frame.Size.Height - bottomPoint));
}
else
{
scrollamount = 0;
}
//Check to see whether the view
//should be moved up...
if (scrollamount > 0) {
moveViewUp = true;
ScrollTheView (moveViewUp);
} else
moveViewUp = false;
}
private void ResetTheView ()
{
UIView.BeginAnimations (string.Empty, System.IntPtr.Zero);
UIView.SetAnimationDuration (0.3);
RectangleF frame = View.Frame;
frame.Y = 0;
View.Frame = frame;
UIView.CommitAnimations ();
}
private void ScrollTheView (bool movedUp)
{
//To invoke a views built-in animation behaviour,
//you create an animation block and
//set the duration of the move...
//Set the display scroll animation and duration...
UIView.BeginAnimations (string.Empty, System.IntPtr.Zero);
UIView.SetAnimationDuration (0.3);
//Get Display size...
RectangleF frame = View.Frame;
if (movedUp) {
//If the view should be moved up,
//subtract the keyboard height from the display...
frame.Y -= scrollamount;
} else {
//If the view shouldn't be moved up, restore it
//by adding the keyboard height back to the original...
frame.Y += scrollamount;
}
//Assign the new frame to the view...
View.Frame = frame;
//Tell the view that your all done with setting
//the animation parameters, and it should
//start the animation...
UIView.CommitAnimations ();
}
}
}
The Recent Version - Still The Same User Experience' slow!
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace IstanbulCity
{
public partial class AskForNAme : UIViewController
{
public delegate void AskForNAmeClosingDelegate (AskForNAme form);
public event AskForNAmeClosingDelegate AskForNAmeClosed;
public AskForNAme () : base ("AskForNAme", 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 ();
// Perform any additional setup after loading the view, typically from a nib.
}
public override void ViewDidUnload ()
{
base.ViewDidUnload ();
// Clear any references to subviews of the main view in order to
// allow the Garbage Collector to collect them sooner.
//
// e.g. myOutlet.Dispose (); myOutlet = null;
ReleaseDesignerOutlets ();
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
// Return true for supported orientations
return (toInterfaceOrientation != UIInterfaceOrientation.PortraitUpsideDown);
}
void HandleIstanbulCityViewControllerClosed (babyAge form)
{
form.DismissModalViewControllerAnimated (true);
form = null;
}
}
}
This does not look to be initialization related. You are adding notifications from both ViewDidAppear and ViewWillAppear. You're also always calling ResetTheView, which does animations, on every keyboard notification (even if nothing else changed).
My guess is that you are calling ResetTheView way more often that you realize - and the continuous animations are killing the performance of your application.
You can confirm this by putting a Console.WriteLine, and maybe a counter, in the ResetTheView method.

Resources