-
-
Save sushihangover/b61c220053cdfee5fab205d6a44727cd to your computer and use it in GitHub Desktop.
Parent view controller to handle keyboard notifications to center views in the screen. UIScrollView related methods moved to an extension class.
This file contains 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 Foundation; | |
using UIKit; | |
using TwinCoders.TouchUtils.Extensions; | |
using CoreGraphics; | |
namespace SalesForce.Touch.Views | |
{ | |
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, CGRect 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 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 UIKit; | |
using CoreGraphics; | |
namespace TwinCoders.TouchUtils.Extensions | |
{ | |
public static class ScrollExtensions | |
{ | |
public static void CenterView(this UIScrollView scrollView, UIView viewToCenter, CGRect keyboardFrame, bool animated = false) { | |
var scrollFrame = scrollView.Frame; | |
var adjustedFrame = UIApplication.SharedApplication.KeyWindow.ConvertRectFromView(scrollFrame, scrollView.Superview); | |
var intersect = CGRect.Intersect(adjustedFrame, keyboardFrame); | |
var height = intersect.Height; | |
if (!UIDevice.CurrentDevice.CheckSystemVersion(8, 0) && IsLandscape()) { | |
height = intersect.Width; | |
} | |
scrollView.CenterView(viewToCenter, height, animated:animated); | |
} | |
public static void CenterView(this UIScrollView scrollView, UIView viewToCenter, nfloat keyboardHeight = default(nfloat), 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 | |
CGRect 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 CGPoint(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 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 UIKit; | |
using Foundation; | |
namespace TwinCoders.TouchUtils.Extensions | |
{ | |
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; | |
} | |
public static UIMotionEffect SetParallaxIntensity(this UIView view, float parallaxDepth, float? verticalDepth = null) { | |
if (UIDevice.CurrentDevice.CheckSystemVersion(7, 0)) | |
{ | |
float vertical = verticalDepth ?? parallaxDepth; | |
var verticalMotionEffect = new UIInterpolatingMotionEffect("center.y", UIInterpolatingMotionEffectType.TiltAlongVerticalAxis); | |
verticalMotionEffect.MinimumRelativeValue = new NSNumber(-vertical); | |
verticalMotionEffect.MaximumRelativeValue = new NSNumber(vertical); | |
var horizontalMotionEffect = new UIInterpolatingMotionEffect("center.x", UIInterpolatingMotionEffectType.TiltAlongHorizontalAxis); | |
horizontalMotionEffect.MinimumRelativeValue = new NSNumber(-parallaxDepth); | |
horizontalMotionEffect.MaximumRelativeValue = new NSNumber(parallaxDepth); | |
var group = new UIMotionEffectGroup(); | |
group.MotionEffects = new UIMotionEffect[]{ horizontalMotionEffect, verticalMotionEffect }; | |
view.AddMotionEffect(group); | |
return group; | |
} | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment