Forked from redent/ParentViewController.Keyboard.cs
Last active
August 29, 2015 14:06
-
-
Save topgenorth/f27887a5bbfcf8dc165d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using MonoTouch.UIKit; | |
using MonoTouch.Foundation; | |
using System.Drawing; | |
namespace TwinCoders.Utils | |
{ | |
public abstract partial class ParentViewController | |
{ | |
/// <summary> | |
/// Call this method from constructor, ViewDidLoad or ViewWillAppear to enable keyboard handling in the main partial class | |
/// </summary> | |
void InitKeyboardHandling() | |
{ | |
//Only do this if required | |
if (HandlesKeyboardNotifications()) { | |
RegisterForKeyboardNotifications(); | |
} | |
} | |
/// <summary> | |
/// Set this field to any view inside the textfield to center this view instead of the current responder | |
/// </summary> | |
protected UIView ViewToCenterOnKeyboardShown; | |
protected UIScrollView ScrollToCenterOnKeyboardShown; | |
/// <summary> | |
/// Override point for subclasses, return true if you want to handle keyboard notifications | |
/// to center the active responder in the scroll above the keyboard when it appears | |
/// </summary> | |
public virtual bool HandlesKeyboardNotifications() | |
{ | |
return false; | |
} | |
NSObject _keyboardShowObserver; | |
NSObject _keyboardHideObserver; | |
protected virtual void RegisterForKeyboardNotifications() | |
{ | |
if (_keyboardShowObserver == null) | |
_keyboardShowObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, OnKeyboardNotification); | |
if (_keyboardHideObserver == null) | |
_keyboardHideObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, OnKeyboardNotification); | |
} | |
protected virtual void UnregisterForKeyboardNotifications() | |
{ | |
if (_keyboardShowObserver != null) | |
{ | |
NSNotificationCenter.DefaultCenter.RemoveObserver(_keyboardShowObserver); | |
_keyboardShowObserver.Dispose(); | |
_keyboardShowObserver = null; | |
} | |
if (_keyboardHideObserver != null) | |
{ | |
NSNotificationCenter.DefaultCenter.RemoveObserver(_keyboardHideObserver); | |
_keyboardHideObserver.Dispose(); | |
_keyboardHideObserver = null; | |
} | |
} | |
/// <summary> | |
/// Gets the UIView that represents the "active" user input control (e.g. textfield, or button under a text field) | |
/// </summary> | |
/// <returns> | |
/// A <see cref="UIView"/> | |
/// </returns> | |
protected virtual UIView KeyboardGetActiveView() | |
{ | |
return View.FindFirstResponder(); | |
} | |
private void OnKeyboardNotification (NSNotification notification) | |
{ | |
if (!IsViewLoaded) return; | |
//Check if the keyboard is becoming visible | |
var visible = notification.Name == UIKeyboard.WillShowNotification; | |
//Start an animation, using values from the keyboard | |
UIView.BeginAnimations ("AnimateForKeyboard"); | |
UIView.SetAnimationBeginsFromCurrentState (true); | |
UIView.SetAnimationDuration (UIKeyboard.AnimationDurationFromNotification (notification)); | |
UIView.SetAnimationCurve ((UIViewAnimationCurve)UIKeyboard.AnimationCurveFromNotification (notification)); | |
//Pass the notification, calculating keyboard height, etc. | |
var keyboardFrame = visible | |
? UIKeyboard.FrameEndFromNotification(notification) | |
: UIKeyboard.FrameBeginFromNotification(notification); | |
OnKeyboardChanged (visible, keyboardFrame); | |
//Commit the animation | |
UIView.CommitAnimations (); | |
} | |
/// <summary> | |
/// Override this method to apply custom logic when the keyboard is shown/hidden | |
/// </summary> | |
/// <param name='visible'> | |
/// If the keyboard is visible | |
/// </param> | |
/// <param name='keyboardFrame'> | |
/// Frame of the keyboard | |
/// </param> | |
protected virtual void OnKeyboardChanged (bool visible, RectangleF keyboardFrame) | |
{ | |
var activeView = ViewToCenterOnKeyboardShown ?? KeyboardGetActiveView(); | |
if (activeView == null) | |
return; | |
var scrollView = ScrollToCenterOnKeyboardShown ?? | |
activeView.FindTopSuperviewOfType(View, typeof(UIScrollView)) as UIScrollView; | |
if (scrollView == null) | |
return; | |
if (!visible) | |
scrollView.RestoreScrollPosition(); | |
else | |
scrollView.CenterView(activeView, keyboardFrame); | |
} | |
/// <summary> | |
/// Call it to force dismiss keyboard when background is tapped | |
/// </summary> | |
protected void DismissKeyboardOnBackgroundTap() | |
{ | |
// Add gesture recognizer to hide keyboard | |
var tap = new UITapGestureRecognizer { CancelsTouchesInView = false }; | |
tap.AddTarget(() => View.EndEditing(true)); | |
tap.ShouldReceiveTouch = (recognizer, touch) => | |
!(touch.View is UIControl || touch.View.FindSuperviewOfType(View, typeof(UITableViewCell)) != null); | |
View.AddGestureRecognizer(tap); | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using MonoTouch.UIKit; | |
using System.Drawing; | |
namespace TwinCoders.Utils | |
{ | |
public static class ScrollExtensions | |
{ | |
public static void CenterView(this UIScrollView scrollView, UIView viewToCenter, RectangleF keyboardFrame, bool animated = false) { | |
var scrollFrame = scrollView.Frame; | |
var adjustedFrame = UIApplication.SharedApplication.KeyWindow.ConvertRectFromView(scrollFrame, scrollView.Superview); | |
var intersect = RectangleF.Intersect(adjustedFrame, keyboardFrame); | |
scrollView.CenterView(viewToCenter, IsLandscape() ? intersect.Width : intersect.Height, animated:animated); | |
} | |
public static void CenterView(this UIScrollView scrollView, UIView viewToCenter, float keyboardHeight = 0, bool adjustContentInsets = true, bool animated = false) | |
{ | |
if (adjustContentInsets) | |
{ | |
var contentInsets = new UIEdgeInsets(0.0f, 0.0f, keyboardHeight, 0.0f); | |
scrollView.ContentInset = contentInsets; | |
scrollView.ScrollIndicatorInsets = contentInsets; | |
} | |
// Position of the active field relative isnside the scroll view | |
RectangleF relativeFrame = viewToCenter.Superview.ConvertRectToView(viewToCenter.Frame, scrollView); | |
var spaceAboveKeyboard = scrollView.Frame.Height - keyboardHeight; | |
// Move the active field to the center of the available space | |
var offset = relativeFrame.Y - (spaceAboveKeyboard - viewToCenter.Frame.Height) / 2; | |
if (scrollView.ContentOffset.Y < offset) { | |
scrollView.SetContentOffset(new PointF(0, offset), animated); | |
} | |
} | |
public static void RestoreScrollPosition(this UIScrollView scrollView) | |
{ | |
scrollView.ContentInset = UIEdgeInsets.Zero; | |
scrollView.ScrollIndicatorInsets = UIEdgeInsets.Zero; | |
} | |
public static bool IsLandscape() { | |
var orientation = UIApplication.SharedApplication.StatusBarOrientation; | |
bool landscape = orientation == UIInterfaceOrientation.LandscapeLeft | |
|| orientation == UIInterfaceOrientation.LandscapeRight; | |
return landscape; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using MonoTouch.UIKit; | |
namespace TwinCoders.Utils | |
{ | |
public static class ViewExtensions | |
{ | |
/// <summary> | |
/// Find the first responder in the <paramref name="view"/>'s subview hierarchy | |
/// </summary> | |
/// <param name="view"> | |
/// A <see cref="UIView"/> | |
/// </param> | |
/// <returns> | |
/// A <see cref="UIView"/> that is the first responder or null if there is no first responder | |
/// </returns> | |
public static UIView FindFirstResponder(this UIView view) | |
{ | |
if (view.IsFirstResponder) | |
{ | |
return view; | |
} | |
foreach (UIView subView in view.Subviews) | |
{ | |
var firstResponder = subView.FindFirstResponder(); | |
if (firstResponder != null) | |
return firstResponder; | |
} | |
return null; | |
} | |
/// <summary> | |
/// Find the first Superview of the specified type (or descendant of) | |
/// </summary> | |
/// <param name="view"> | |
/// A <see cref="UIView"/> | |
/// </param> | |
/// <param name="stopAt"> | |
/// A <see cref="UIView"/> that indicates where to stop looking up the superview hierarchy | |
/// </param> | |
/// <param name="type"> | |
/// A <see cref="Type"/> to look for, this should be a UIView or descendant type | |
/// </param> | |
/// <returns> | |
/// A <see cref="UIView"/> if it is found, otherwise null | |
/// </returns> | |
public static UIView FindSuperviewOfType(this UIView view, UIView stopAt, Type type) | |
{ | |
if (view.Superview != null) | |
{ | |
if (type.IsInstanceOfType(view.Superview)) | |
{ | |
return view.Superview; | |
} | |
if (view.Superview != stopAt) | |
return view.Superview.FindSuperviewOfType(stopAt, type); | |
} | |
return null; | |
} | |
public static UIView FindTopSuperviewOfType(this UIView view, UIView stopAt, Type type) | |
{ | |
var superview = view.FindSuperviewOfType(stopAt, type); | |
var topSuperView = superview; | |
while (superview != null && superview != stopAt) | |
{ | |
superview = superview.FindSuperviewOfType(stopAt, type); | |
if (superview != null) | |
topSuperView = superview; | |
} | |
return topSuperView; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment