I have application where there is chat page. On the page, I use ListView
with DataTemplate
to show incoming and outgoing message. Now the problem is when I click on Entry(Textbox) where user can write message, keyboard opens and my ListView keep space between last message and Entry.
I have read few blogs and example of chat but none having consistent behavior.
Code:
<ContentPage.Resources>
<ResourceDictionary>
<local:MessageTemplateSelector x:Key="MessageTemplateSelector" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<ListView x:Name="MessagesListView"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Messages}"
HasUnevenRows="True"
ItemSelected="MyListView_OnItemSelected"
ItemTapped="MyListView_OnItemTapped"
IsPullToRefreshEnabled="true"
IsRefreshing="{Binding IsRefreshing}"
RefreshCommand="{Binding RefreshCommand}"
SeparatorVisibility="None"
BackgroundColor="{DynamicResource d8GreyLight}"
//RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=1,Constant=0}"
/>
<Grid x:Name="MessageControls" RowSpacing="1" ColumnSpacing="2" Padding="5"
BackgroundColor="#EFEFF4"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Entry x:Name="txtMessage" Grid.Column="0" HeightRequest="40" Placeholder="Message" Text="{Binding OutGoingText}" TextChanged="EnableSend"/>
<Frame x:Name="SendButton">
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="SendMessage_Click" NumberOfTapsRequired="1" />
</Frame.GestureRecognizers>
<Label Text="Send"/>
</Frame>
</Grid>
</StackLayout>
</ContentPage.Content>
Way-1: KeyboardRendering for MessagePage: Keep Space between ListView last message and SendTextBox
using Project;
using Project.iOS;
using CoreGraphics;
using Foundation;
using System;
using UIKit;
using Xamarin.Forms;
[assembly: ExportRenderer(typeof(MessagePage), typeof(KeyboardAdaptedPageRenderer))]
namespace Project.iOS
{
public class KeyboardAdaptedPageRenderer : ContentPageWithCustomBackButtonRenderer
{
//Way-1: Keep Space between ListView last message and SendTextBox
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
Keyboard.WillShow += OnKeyboardWillShow;
Keyboard.WillHide += OnKeyboardWillHide;
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(animated);
Keyboard.WillShow -= OnKeyboardWillShow;
Keyboard.WillHide -= OnKeyboardWillHide;
OnKeyboardWillHide(new KeyboardInfo()
{
AnimationDuration = 0,
AnimatonOptions = UIViewAnimationOptions.TransitionNone
});
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
Keyboard.WillShow -= OnKeyboardWillShow;
Keyboard.WillHide -= OnKeyboardWillHide;
}
private void OnKeyboardWillShow(KeyboardInfo info)
{
NSThread.SleepFor(1);
if (info.SoftwareKeyboardIsVisible)
{
UIView.Animate(info.AnimationDuration, 0, info.AnimatonOptions, () =>
{
var bounds = View.Bounds;
bounds.Y = info.BeginRect.Top - info.EndRect.Top; // iphone 4 and others
View.Bounds = bounds;
}, null);
}
}
private void OnKeyboardWillHide(KeyboardInfo info)
{
NSThread.SleepFor(1);
UIView.Animate(info.AnimationDuration, 0, info.AnimatonOptions, () =>
{
var bounds = View.Bounds;
bounds.Y = 0;
View.Bounds = bounds;
}, null);
}
}
}
Result
Way-2: KeyboardRendering for MessagePage: Design of SendTextBox issue when keyboard up and down
using Project;
using Project.iOS;
using CoreGraphics;
using Foundation;
using System;
using UIKit;
using Xamarin.Forms;
[assembly: ExportRenderer(typeof(MessagePage), typeof(KeyboardAdaptedPageRenderer))]
namespace Project.iOS
{
public class KeyboardAdaptedPageRenderer : ContentPageWithCustomBackButtonRenderer
{
//way-2 Error: Space issue is solved but not consistant in design of SendTextbox in way-1
NSObject observerHideKeyboard;
NSObject observerShowKeyboard;
public override void ViewDidLoad()
{
base.ViewDidLoad();
var cp = Element as ConversationPage;
if (cp != null && !cp.CancelsTouchesInView)
{
foreach (var g in View.GestureRecognizers)
{
g.CancelsTouchesInView = false;
}
}
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
observerHideKeyboard = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, OnKeyboardNotification);
observerShowKeyboard = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, OnKeyboardNotification);
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
NSNotificationCenter.DefaultCenter.RemoveObserver(observerHideKeyboard);
NSNotificationCenter.DefaultCenter.RemoveObserver(observerShowKeyboard);
}
void OnKeyboardNotification(NSNotification notification)
{
if (!IsViewLoaded) return;
var frameBegin = UIKeyboard.FrameBeginFromNotification(notification);
var frameEnd = UIKeyboard.FrameEndFromNotification(notification);
var page = Element as ContentPage;
if (page != null && !(page.Content is ScrollView))
{
var padding = page.Padding;
NSThread.SleepFor(1);
page.Padding = new Thickness(padding.Left, padding.Top, padding.Right, padding.Bottom + (frameBegin.Top - frameEnd.Top));
}
}
}
}