I'm building a xamarin.forms chat application and need add only the new element that is delivered, on Android I have the expected behavior, but iOS refresh all the CollectionView what causes a bad user experience. Please, have a look at my code(relevant portion only) and let me know what I'm doing wrong or what I missed to have this behavior on iOS too.
My .xaml:
<CollectionView x:Name="ChatCollectionView"
SelectionMode="None" HorizontalScrollBarVisibility="Never"
VerticalScrollBarVisibility="Always" ItemsUpdatingScrollMode="KeepLastItemInView"
ItemTemplate="{StaticResource MsgTemplateSelector}" Margin="5,0,5,10">
</CollectionView>
The .cs file:
public static ReaderWriterLockSlim valuesLock = new ReaderWriterLockSlim();
void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess)
{
var collectionLock = (ReaderWriterLockSlim)context;
Action enterLock = writeAccess ? new Action(collectionLock.EnterWriteLock) : new Action(collectionLock.EnterReadLock);
Action exitLock = writeAccess ? new Action(collectionLock.ExitWriteLock) : new Action(collectionLock.ExitReadLock);
enterLock();
try { accessMethod(); } finally { exitLock(); }
}
ObservableCollection<Msg> ListMsg;
public Page(string userid, string chatkey, string titulo, string foto)
{
// some code ...
this.BindingContext = this;
BindingBase.EnableCollectionSynchronization(ListMsg as IEnumerable, valuesLock,ObservableCollectionCallback);
List<Msg> recipient = await Task.Run(()=> client.PostData<List<Msg>>("url/msg.php"));
//previous line returns a json and deserialize it to a list of "Msg"
if (recipient != null)
{
ListMsg = new ObservableCollection<Msg>(recipient);
ChatCollectionView.ItemsSource = ListMsg;
if (ListMsg.Count > 0)
{
if(Device.RuntimePlatform==Device.iOS)
await Task.Delay(2000);
ChatCollectionView.ScrollTo(ListMsg.Count - 1, position: ScrollToPosition.End, animate: false);
}
}
else
ChatCollectionView.ItemsSource = new ObservableCollection<Msg>();
MessagingCenter.Subscribe<MainPage, Msg>("Page", "scroll", async (sender, arg) =>
{
Task.WaitAll(tasks.ToArray());
tasks.Add(Task.Run(async () =>
{
arg = await MsgPrepare(arg); //this only changes the element arg not the list
_ = Device.InvokeOnMainThreadAsync(() =>
{
valuesLock.EnterWriteLock();
try { ListMsg.Add(arg); }
finally { valuesLock.ExitWriteLock(); }
});
}));
}
}
Fixed it by "mistake". I added involving the whole page to fix an issue of iOS soft keyboard(was covering the focused Entry element) and bingo, it has fixed these two problems. If I take off the ScrollView, the problem is back. I also have noticed that it works if ScrollView is containing only the CollectionView, it doesn't need envolving whole page as in my case. Other point is if you don't want the scrolling feature of the ScrollView, you can set Orientation=Neither to avoid it. Hope this help you guys.