Xamarin.iOS:在键盘上方移动工具栏

时间:2016-08-14 05:28:30

标签: ios uinavigationcontroller keyboard xamarin.ios uitoolbar

我正在使用此示例代码创建一个简单的聊天。

Chat

这个例子没有NavigationController,一切正常。当键盘出现时,工具栏会向上移动。但是当我把它放在NavigationController中时,键盘不会移动(但是View会移动)。我尝试将textview inputAccessoryView设置为键盘,但无法正常工作。堆栈溢出中的其他解决方案涉及使用导航控制器的工具栏时。但这是一个自定义创建的工具栏...

感谢任何帮助...

public class ChatViewController : UIViewController
{
    public MessageContacts chatWith;

    NSObject willShowToken;
    NSObject willHideToken;

    List<Message> messages;
    ChatSource chatSource;

    UITableView tableView;
    UIToolbar toolbar;

    NSLayoutConstraint toolbarBottomConstraint;
    NSLayoutConstraint toolbarHeightConstraint;

    ChatInputView chatInputView;

    UIButton SendButton
    {
        get
        {
            return chatInputView.SendButton;
        }
    }

    UITextView TextView
    {
        get
        {
            return chatInputView.TextView;
        }
    }

    bool IsInputToolbarHasReachedMaximumHeight
    {
        get
        {
            return toolbar.Frame.GetMinY() == TopLayoutGuide.Length;
        }
    }

    public ChatViewController(IntPtr handle)
        : base(handle)
    {
        messages = new List<Message>() {
            new Message { Type = MessageType.Incoming, Text = "Hello!" },
            new Message { Type = MessageType.Outgoing, Text = "Hi!" },
            new Message { Type = MessageType.Incoming, Text = "Do you know about Xamarin?" },
            new Message { Type = MessageType.Outgoing, Text = "Yes! Sure!" },
            new Message { Type = MessageType.Incoming, Text = "And what do you think?" },
            new Message { Type = MessageType.Outgoing, Text = "I think it is the best way to develop mobile applications." },
            new Message { Type = MessageType.Incoming, Text = "Wow :-)" },
            new Message { Type = MessageType.Outgoing, Text = "Yep. Check it out\nhttp://Xamarin.com" },
            new Message { Type = MessageType.Incoming, Text = "Will do. Thanks" },
        };
    }

    #region Life cycle

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        Title = chatWith.name;
        SetUpTableView();
        SetUpToolbar();

        SendButton.TouchUpInside += OnSendClicked;
        TextView.Started += OnTextViewStarted;
        TextView.Changed += OnTextChanged;

        TextView.InputAccessoryView = toolbar;
    }

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

        willShowToken = UIKeyboard.Notifications.ObserveWillShow(KeyboardWillShowHandler);
        willHideToken = UIKeyboard.Notifications.ObserveWillHide(KeyboardWillHideHandler);

        UpdateTableInsets();
        UpdateButtonState();
        ScrollToBottom(false);
    }

    public override void ViewDidAppear(bool animated)
    {
        base.ViewDidAppear(animated);
        AddObservers();
    }

    #endregion

    #region Initialization

    void SetUpTableView()
    {
        tableView = new UITableView
        {
            TranslatesAutoresizingMaskIntoConstraints = false,
            AllowsSelection = false,
            SeparatorStyle = UITableViewCellSeparatorStyle.None,
        };
        tableView.RegisterClassForCellReuse(typeof(IncomingCell), IncomingCell.CellId);
        tableView.RegisterClassForCellReuse(typeof(OutgoingCell), OutgoingCell.CellId);
        View.AddSubview(tableView);

        var pinLeft = NSLayoutConstraint.Create(tableView, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, View, NSLayoutAttribute.Leading, 1f, 0f);
        View.AddConstraint(pinLeft);

        var pinRight = NSLayoutConstraint.Create(tableView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, View, NSLayoutAttribute.Trailing, 1f, 0f);
        View.AddConstraint(pinRight);

        var pinTop = NSLayoutConstraint.Create(tableView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, TopLayoutGuide, NSLayoutAttribute.Bottom, 1f, 0f);
        View.AddConstraint(pinTop);

        var pinBottom = NSLayoutConstraint.Create(tableView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, View, NSLayoutAttribute.Bottom, 1f, 0f);
        View.AddConstraint(pinBottom);

        chatSource = new ChatSource(messages);
        tableView.Source = chatSource;
    }

    void SetUpToolbar()
    {
        toolbar = new UIToolbar
        {
            TranslatesAutoresizingMaskIntoConstraints = false
        };
        chatInputView = new ChatInputView
        {
            TranslatesAutoresizingMaskIntoConstraints = false
        };

        View.AddSubview(toolbar);

        var pinLeft = NSLayoutConstraint.Create(toolbar, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, View, NSLayoutAttribute.Leading, 1f, 0f);
        View.AddConstraint(pinLeft);

        var pinRight = NSLayoutConstraint.Create(toolbar, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, View, NSLayoutAttribute.Trailing, 1f, 0f);
        View.AddConstraint(pinRight);

        toolbarBottomConstraint = NSLayoutConstraint.Create(View, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, toolbar, NSLayoutAttribute.Bottom, 1f, 0f);
        View.AddConstraint(toolbarBottomConstraint);

        toolbarHeightConstraint = NSLayoutConstraint.Create(toolbar, NSLayoutAttribute.Height, NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 0f, 44f);
        View.AddConstraint(toolbarHeightConstraint);

        toolbar.AddSubview(chatInputView);
        var c1 = NSLayoutConstraint.FromVisualFormat("H:|[chat_container_view]|",
            (NSLayoutFormatOptions)0,
            "chat_container_view", chatInputView
        );
        var c2 = NSLayoutConstraint.FromVisualFormat("V:|[chat_container_view]|",
            (NSLayoutFormatOptions)0,
            "chat_container_view", chatInputView
        );
        toolbar.AddConstraints(c1);
        toolbar.AddConstraints(c2);


    }

    #endregion

    void AddObservers()
    {
        TextView.AddObserver(this, "contentSize", NSKeyValueObservingOptions.OldNew, IntPtr.Zero);
    }

    public override void ObserveValue(NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context)
    {
        if (keyPath == "contentSize")
            OnSizeChanged(new NSObservedChange(change));
        else
            base.ObserveValue(keyPath, ofObject, change, context);
    }

    void OnSizeChanged(NSObservedChange change)
    {
        CGSize oldValue = ((NSValue)change.OldValue).CGSizeValue;
        CGSize newValue = ((NSValue)change.NewValue).CGSizeValue;

        var dy = newValue.Height - oldValue.Height;
        AdjustInputToolbarOnTextViewSizeChanged(dy);
    }

    void AdjustInputToolbarOnTextViewSizeChanged(nfloat dy)
    {
        bool isIncreasing = dy > 0;
        if (IsInputToolbarHasReachedMaximumHeight && isIncreasing)
        {
            // TODO: scroll to bottom
            return;
        }

        nfloat oldY = toolbar.Frame.GetMinY();
        nfloat newY = oldY - dy;
        if (newY < TopLayoutGuide.Length)
            dy = oldY - TopLayoutGuide.Length;

        AdjustInputToolbar(dy);
    }

    void AdjustInputToolbar(nfloat change)
    {
        toolbarHeightConstraint.Constant += change;

        if (toolbarHeightConstraint.Constant < ChatInputView.ToolbarMinHeight)
            toolbarHeightConstraint.Constant = ChatInputView.ToolbarMinHeight;

        View.SetNeedsUpdateConstraints();
        View.LayoutIfNeeded();
    }

    void KeyboardWillShowHandler(object sender, UIKeyboardEventArgs e)
    {
        UpdateButtomLayoutConstraint(e);
    }

    void KeyboardWillHideHandler(object sender, UIKeyboardEventArgs e)
    {
        UpdateButtomLayoutConstraint(e);
    }

    void UpdateButtomLayoutConstraint(UIKeyboardEventArgs e)
    {
        UIViewAnimationCurve curve = e.AnimationCurve;
        UIView.Animate(e.AnimationDuration, 0, ConvertToAnimationOptions(e.AnimationCurve), () =>
        {
            nfloat offsetFromBottom = tableView.Frame.GetMaxY() - e.FrameEnd.GetMinY();
            offsetFromBottom = NMath.Max(0, offsetFromBottom);
            SetToolbarContstraint(offsetFromBottom);
        }, null);
    }

    void SetToolbarContstraint(nfloat constant)
    {
        toolbarBottomConstraint.Constant = constant;
        View.SetNeedsUpdateConstraints();
        View.LayoutIfNeeded();

        UpdateTableInsets();
    }

    void UpdateTableInsets()
    {
        nfloat bottom = tableView.Frame.GetMaxY() - toolbar.Frame.GetMinY();
        UIEdgeInsets insets = new UIEdgeInsets(0f, 0f, bottom, 0f);
        tableView.ContentInset = insets;
        tableView.ScrollIndicatorInsets = insets;
    }

    UIViewAnimationOptions ConvertToAnimationOptions(UIViewAnimationCurve curve)
    {
        // Looks like a hack. But it is correct.
        // UIViewAnimationCurve and UIViewAnimationOptions are shifted by 16 bits
        // http://stackoverflow.com/questions/18870447/how-to-use-the-default-ios7-uianimation-curve/18873820#18873820
        return (UIViewAnimationOptions)((int)curve << 16);
    }

    void OnSendClicked(object sender, EventArgs e)
    {
        var text = TextView.Text;
        TextView.Text = string.Empty; // this will not generate change text event
        UpdateButtonState();

        if (string.IsNullOrWhiteSpace(text))
            return;

        var msg = new Message
        {
            Type = MessageType.Outgoing,
            Text = text.Trim()
        };

        messages.Add(msg);
        tableView.InsertRows(new NSIndexPath[] { NSIndexPath.FromRowSection(messages.Count - 1, 0) }, UITableViewRowAnimation.None);
        ScrollToBottom(true);
    }

    void OnTextViewStarted(object sender, EventArgs e)
    {
        ScrollToBottom(true);
    }

    void OnTextChanged(object sender, EventArgs e)
    {
        UpdateButtonState();
    }

    void UpdateButtonState()
    {
        SendButton.Enabled = !string.IsNullOrWhiteSpace(TextView.Text);
    }

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

        willShowToken.Dispose();
        willHideToken.Dispose();
    }

    void ScrollToBottom(bool animated)
    {
        if (tableView.NumberOfSections() == 0)
            return;

        int items = (int)tableView.NumberOfRowsInSection(0);
        if (items == 0)
            return;

        int finalRow = (int)NMath.Max(0, tableView.NumberOfRowsInSection(0) - 1);
        NSIndexPath finalIndexPath = NSIndexPath.FromRowSection(finalRow, 0);
        tableView.ScrollToRow(finalIndexPath, UITableViewScrollPosition.Top, animated);
    }
}

1 个答案:

答案 0 :(得分:0)

这个样本真的很糟糕,有很多bug。

对于键盘问题,我建议您使用这些代码:

在ViewDidLoad中首次注册事件:

public override void ViewDidLoad ()
    {
        //Smaples codes
        base.ViewDidLoad ();

        SetUpTableView ();
        SetUpToolbar ();

        SendButton.TouchUpInside += OnSendClicked;
        TextView.Started += OnTextViewStarted;
        TextView.Changed += OnTextChanged;

        //Create by me
        this.View.AddGestureRecognizer(new UITapGestureRecognizer(()=>{
            this.View.EndEditing(true);
        }));

        NSNotificationCenter.DefaultCenter.AddObserver(this,new ObjCRuntime.Selector("keyboardWillShow:"),UIKeyboard.WillShowNotification,null);
        NSNotificationCenter.DefaultCenter.AddObserver(this,new ObjCRuntime.Selector("keyboardWillHide:"),UIKeyboard.WillHideNotification,null);
    }

然后使用它们处理键盘事件:

[Action("keyboardWillShow:")]
    private void KeyboardWillShow(NSNotification notification)
    {
        NSDictionary info = notification.UserInfo;
        CGSize kbSize = (info.ObjectForKey(UIKeyboard.FrameEndUserInfoKey) as NSValue).CGRectValue.Size;
        nfloat keyboardHeight = kbSize.Height;

        CGRect tmpR = this.View.Frame;
        tmpR.Y -= keyboardHeight;
        this.View.Frame = tmpR;
    }   

    [Action("keyboardWillHide:")]
    private void KeyboardWillHide(NSNotification notification)
    {
        this.View.Frame = UIScreen.MainScreen.Bounds;
    }   

有一个非常简单的聊天视图模型我以前写过另一个,由Objective-C制作,你可以看看,非常简单。

simple model for chat view

希望它可以帮助你,如果你仍然需要一些帮助,就把它放在这里,我会检查它。