Karen Cate Karen Cate - 2 months ago 10
C# Question

Xamarin solution for: UITableView and keyboard scrolling issue

I was having the same issue as described in UITableView and keyboard scrolling issue but I'm coding in C# using MonoTouch/Xamarin.iOS. It took a little bit of work to get a working C# solution, so I thought I would share.

The problem I was trying to solve: I have a UIViewController that contains a list view as well as a few other buttons. The rows are themselves custom views that contain several inert UILabels and one UITextView that accepts input. If the user touched one of the TextViews in the bottom half of the screen, the keyboard would cover the field they were editing. And, furthermore, if it was the end of the list, you couldn't even manually scroll it into view.

I've gotten so much help from reading the posts on this site, that I wanted to give some back. From pulling together bits from several posts, I think I have a (reasonably) simple, working C# solution. I don't have enough reputation to post a comment, so I'm creating a question and answering hoping that others might save some time. See below for my solution, and please let me know if I'm missing something!

Answer

Here are just the relevant code to adjust for the keyboard, and dismiss the keyboard when the user taps outside of the TextView (and keyboard).

I should note that the caveat is that my TableView is at the very bottom of the screen, so I don't have to figure out how it overlaps with the keyboard. If you have stuff below your TableView, you'll need to add some math.

public partial class myViewController : UIViewController
{
    UITapGestureRecognizer _tap;
    NSObject _shownotification;
    NSObject _hidenotification;

    public myViewController() : base("myViewController", null)
    {
        // This code dismisses the keyboard when the user touches anywhere
        // outside the keyboard.
        _tap = new UITapGestureRecognizer();
        _tap.AddTarget(() =>{
            View.EndEditing(true);
        });
        _tap.CancelsTouchesInView = false;
        View.AddGestureRecognizer(_tap);
    }

    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);

        // Register our callbacks
        _hidenotification = UIKeyboard.Notifications.ObserveDidHide(HideCallback);
        _shownotification = UIKeyboard.Notifications.ObserveWillShow(ShowCallback);
    }

    public override void ViewWillDisappear(bool animated)
    {
        // Unregister the callbacks
        if (_shownotification != null)
            _shownotification.Dispose();
        if (_hidenotification != null)
            _hidenotification.Dispose();

        base.ViewWillDisappear(animated);
    }

    void ShowCallback (object sender, MonoTouch.UIKit.UIKeyboardEventArgs args)
    {
        // This happens if the user focuses a textfield outside of the
        // tableview when the tableview is empty.
        UIView activeView = this.View.FindFirstResponder();
        if ((activeView == null) || (activeView == Customer))
            return;

        // Get the size of the keyboard
        RectangleF keyboardBounds = args.FrameEnd;

        // Create an inset and assign it to the tableview
        UIEdgeInsets contentInsets = new UIEdgeInsets(0.0f, 0.0f, keyboardBounds.Size.Height, 0.0f);
        myTableView.ContentInset = contentInsets;
        myTableView.ScrollIndicatorInsets = contentInsets;

        // Make sure the tapped location is visible.
        myTableView.ScrollRectToVisible(activeView.Frame, true);
    }

    void HideCallback (object sender, MonoTouch.UIKit.UIKeyboardEventArgs args)
    {
        // If the tableView's ContentInset is "zero", we don't need to
        // readjust the size
        if (myTableView.ContentInset.Top == UIEdgeInsets.Zero.Top)
            return;

        // Remove the inset when the keyboard is hidden so that the
        // TableView will use the whole screen again.
        UIView.BeginAnimations (""); {
            UIView.SetAnimationCurve (args.AnimationCurve);
            UIView.SetAnimationDuration (args.AnimationDuration);
            var viewFrame = View.Frame;
            var endRelative = View.ConvertRectFromView (args.FrameEnd, null);
            viewFrame.Height = endRelative.Y;
            View.Frame = viewFrame;

            myTableView.ContentInset = UIEdgeInsets.Zero;
            myTableView.ScrollIndicatorInsets = UIEdgeInsets.Zero;
        } UIView.CommitAnimations ();
    }
}

Thank you mickm for posting the Objective-C solution.