.NET MAUI: entry hidden behind keyboard on iOS - c#

I am trying to get a .NET Maui app up and running and it is working quite well so far - with one major problem: I cannot get it fixed that an Entry/Editor control stays above the keyboard and doesn't get overlapped.
I know there is an open issue and it is in the backlog, but the app seems unusable until this is fixed. There have been a couple of workarounds like
[GitHub] dotnet/maui issue 4792,
[GitHub] dotnet/maui issue 10662 (#1) and
[GitHub] dotnet/maui issue 10662 (#2)
but I cannot get them to work when registering as a handler. #1 doesn't do anything and #2 crashes when loading any view.
There is a solution on StackOverflow as well (Question 72536074) - unfortunately this method ignores the keyboard height.
To reproduce the problem the simplest code example would be any ContentPage with the following content:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="EntryIssueMaui.MainPage">
<ScrollView>
<Entry VerticalOptions="End" BackgroundColor="LightCoral" />
</ScrollView>
</ContentPage>
based on the default Maui template app.
It results in the Entry (red) not being pushed up in the ScrollView when the keyboard is enabled:
How can I implement the - I guess usually default feature - that the control is being pushed up to be still visible while considering the height of the keyboard (e.g. with/without autocompletion or emoji keyboard)?
Thanks in advance!

To solve this issue, you could obtain keyboard's frame via UIKeyboard.FrameEndUserInfoKey and calculate the height need to change.
NSValue result = (NSValue)args.Notification.UserInfo.ObjectForKey(new NSString(UIKeyboard.FrameEndUserInfoKey));
CGSize keyboardSize = result.RectangleFValue.Size;
private void Entry_Focused(object sender, FocusEventArgs e)
{
if (DeviceInfo.Current.Platform == DevicePlatform.iOS)
{
NFloat bottom;
try
{
UIWindow window = UIApplication.SharedApplication.Delegate.GetWindow();
bottom = window.SafeAreaInsets.Bottom;
}
catch
{
bottom = 0;
}
var heightChange = (keyboardSize.Height - bottom);
layout.TranslateTo(0, originalTranslationY.Value - heightChange, 50);
}
}
private void Entry_Unfocused(object sender, FocusEventArgs e)
{
if (DeviceInfo.Current.Platform == DevicePlatform.iOS)
{
layout.TranslateTo(0, 0, 50);
}
}
Hope it works for you.

Related

How to programatically start and stop lottie animations in .NET Maui

I would like to be able to play an animation in my .NET task project whenever I add a task.
Right now I'm able to use SkiaSharp.Extended.UI.Maui to load and animate a lottie file. I can also trigger an animation with a tapgesture. It plays the animation but when I press again it does not play the animation again or it keeps repeating the animation without stopping.
I tried using the property repeatcount. I tried setting it to -1(keep repeating), 0, 1
I have also tried using the duration, isvisible, and isenabled along with a timer. An event gets fired when the duration of the animation has passed and I hide the animation again. That works however only once. I can't get it to run again after that.
Xaml
<skia:SKLottieView
x:Name="animatedPlusIcon"
Source="WhiteCheck.json"
HeightRequest="150"
WidthRequest="150"
RepeatCount="0"
IsAnimationEnabled="True">
<skia:SKLottieView.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</skia:SKLottieView.GestureRecognizers>
</skia:SKLottieView>
Code behind
void TapGestureRecognizer_Tapped(System.Object sender, System.EventArgs e)
{
animatedPlusIcon.IsAnimationEnabled = true;
}
The only workaround i found is: Each time you create a new task, you could remove Lottie first and then add it again with custom settings. Such like the following:
In xaml, add it in a mystack
<StackLayout x:Name="mystack">
<skl:SKLottieView
....
</skl:SKLottieView>
</StackLayout>
Each time you create a task, first remove it and then add it:
(mystack as StackLayout).Clear();
SKLottieView myanimatedview = new SKLottieView();
var a = new SKFileLottieImageSource();
a.File = "dotnetbot.json";
myanimatedview.Source = a;
myanimatedview.RepeatCount = 3;
(mystack as StackLayout).Add(myanimatedview);
For more info, you could refer to SKLottieView. As we can see there is not many APIs for us as it is still a perview. You could also report an issue on Github: SKLottie Issues on Github
Hope it works for you.

Is there a way to prevent a Xamarin Forms scrollview from automatically scrolling to the last edited Entry when a picker control is opened?

I have a setup similar to this:
<ScrollView>
<StackLayout>
<Entry/>
<Picker/>
<Entry/>
<Entry/>
<Entry/>
<Picker/>
<Picker/>
<Entry/>
<Entry/>
<Entry/>
</StackLayout>
</ScrollView>
Where the Entry and Picker controls are dynamically created and modified. The problem is that if I type into an Entry and then open one of the Picker controls, the ScrollView scrolls to where the previously edited Entry is in view. It was getting focus, but I prevented the focus from shifting, yet the ScrollView scrolls and I can't seem to prevent it.
I did something similar to Is it possible to stop the first Entry getting Focus in a ScrollView in Xamarin Forms to fix the related issue in UWP, but that solution is not applicable to iOS and Android.
Set the name of StackLayout. Foreach the elements in 'StackLayout' and reset the focus.
private void Picker_Focused(object sender, FocusEventArgs e)
{
foreach (var item in stackLayout.Children)
{
if (sender!=item)
{
item.Unfocus();
}
}
}

How to make a Xamarin app fullscreen (all screen)

I develop with Xamarin 4.5 and I'm not able to find how to put my application to cover the entire screen (full screen).
For Android and for iOS.
Note: I do not want only an image or a video to cover the entire screen, it should be all the application that should cover the entire screen.
Update 2020-04-29
I found half of the solution, only the Android part (inlcluded in my answer with help of FabriBertani for status bar). I tested it and it works fine. Now I have to find a solution for iPhone (or at least, find a way to test on an iPhone).
On Android add this to the OnCreate method of your MainActivity:
this.Window.AddFlags(WindowManagerFlags.Fullscreen);
For iOS add these values to the info.plist file:
<key>UIStatusBarHidden</key>
<true/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
edit: If you want to remove the toolbar too just add this on your xaml pages:
NavigationPage.HasNavigationBar="False"
Or in the C# code behind
public YourPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
}
If you want to add this to all your pages, I recommend you to create a base page with this and then use this base page in all your pages.
public class BaseContentPage : ContentPage
{
public BaseContentPage
{
NavigationPage.SetHasNavigationBar(this, false);
}
}
And use it on the xaml:
<?xml version="1.0" encoding="UTF-8"?>
<local:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:YourNamespace.Pages"
x:Class="YourNamespace.Pages.YourPage">
</local:BaseContentPage>
This is half of the solution. For Android:
In Android Project. Into MainActivity.OnCreate, add:
this.Window.AddFlags(WindowManagerFlags.Fullscreen); // Hide StatusBar, from FabriBertani
MessagingCenter.Subscribe<Object>(this, "HideOsNavigationBar", (sender) =>
{
int uiOptions = (int)Window.DecorView.SystemUiVisibility;
uiOptions |= (int)SystemUiFlags.HideNavigation;
Window.DecorView.SystemUiVisibility = (StatusBarVisibility) SystemUiFlags.HideNavigation;
});
In Shared project. Into MainPage constructor (I put it after InitializeComponent() but I doubt it is necessary):
MessagingCenter.Send<Object>(this, "HideOsNavigationBar");

VssClientCredentials Interactive Popup shrinks Windows.Forms Elements?

I have a WinForms application I'm designing, and I recently added capability to connect to Azure Devops to get a list of test suites/cases.
VssClientCredentials vcc = new VssClientCredentials(false);
connection = new VssConnection(new Uri(azureDevOpsOrganizationUrl), vcc);
When these lines run, the entire form shrinks down (including text sizes, button and frame sizes) and stay shrunken (not minimized, like 1/4 size). Note, that it becomes smaller the what the minimum size of the form is set to.
Here is a side-by-side comparison of the form size before and after:
https://i.imgur.com/sa9iBAU.png
I also just noticed as I was editing some identifying info out of the picture, that the Title on the Top-Left of the frame also disappeared when it shrunk.
Best I could figure it out, the line
VssClientCredentials vcc = new VssClientCredentials(false);
is the line that triggers the shrinking.
I assume that this might be some function of the popup it's trying to call, but I'm not really sure why.
private void AzureButton_Click (object sender, EventArgs e)
{
GetAzureNodes();
}
private async void GetAzureNodes()
{
AzureConnection azureConn = new AzureConnection(); //<----- Focus Here
await azureConn.getConnection();
if (azureConn.isConnected())
{
//Stuff
}
}
public AzureConnection() //<----- Focus here
{
// Set false to require login popup every time.
VssClientCredentials vcc = new VssClientCredentials(false);
connection = new VssConnection(new Uri(azureDevOpsOrganizationUrl), vcc);
}
public async Task<int> getConnection()
{
await connection.ConnectAsync();
return 0;
}
Anybody know how to either prevent or undo the shrinking?
Or even why it's happening at all?
Problem was resolved by adding an app.manifest file with the following content.
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<!--
Trying to generate the WPF Azure Login Page on a Winforms project suddenly made the app self-awar- I mean, aware of DPI.
That caused it to suddenly shrink. This setting stops that.
-->
<dpiAware>false</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</asmv1:assembly>
I found the answer in this question: https://stackoverflow.com/a/36344413/9866316

Surface sdk 2.0 keyboard not showing in application

I am trying to show the keyboard when I click a button, but it's not showing a keyboard at all.
The "TEST" gets printed but the keyboard isn't showing.
My code is :
private SurfaceTextBox mySurfaceTextBox = new SurfaceTextBox();
void showKeyBoard(object sender, RoutedEventArgs e)
{
//System.Windows.Input.Keyboard.Focus((IInputElement)getCanvasFromButton((SurfaceButton) sender));
System.Windows.Input.Keyboard.Focus((IInputElement)mySurfaceTextBox);
Console.Write("TEST");
SurfaceKeyboard.IsVisible = true;
SurfaceKeyboard.CenterX = (float)InteractiveSurface.PrimarySurfaceDevice.Bounds.Width - (SurfaceKeyboard.Width / 2);
SurfaceKeyboard.CenterY = (float)InteractiveSurface.PrimarySurfaceDevice.Bounds.Height - (SurfaceKeyboard.Height / 2);
SurfaceKeyboard.Layout = Microsoft.Surface.KeyboardLayout.Alphanumeric;
SurfaceKeyboard.Rotation = (float)(Math.PI / 2);
SurfaceKeyboard.ShowsFeedback = false;
}
Can someone help me please?
I don't know much about the surface framework; but usually you cannot force a keyboard to appear, the focused object needs to accept text as an input.
Because buttons generally don't accept text input, the keyboard's focus cannot be given to it, and thus
System.Windows.Input.Keyboard.Focus((IInputElement)sender);
will be ignored.
If the idea is to only make the keyboard appear, then an option is to add a SurfaceTextBox and to give focus to the textbox (this will inturn remove focus from the button)
XAML
Add this to your XAML file
<Canvas>
<s:SurfaceTextBox
Name="yourSurfaceTextBox"
Canvas.Top="200" Canvas.Left="200"
Width="100" Height="40" />
</Canvas>
Code File
void showKeyBoard(object sender, RoutedEventArgs e)
{
Console.Write("TEST");
System.Windows.Input.Keyboard.Focus((IInputElement)yourSurfaceTextBox);
// Rest of your code...
}
If the idea is to get navigation between buttons, you should consider using a SurfaceListBox since it accepts as a default behavior arrow navigation from the keyboard, then your code above should work.
Question in Comments
How I can test this on a non-surface device?
You can use a simulator which should be included in the 2.0 sdk
How I can change the cursor position in the SurfaceTextBox to the place it's touched?
I don't really understand what you mean by 'place it's touched', but you can change the cursor location in the textbox using the select method.
yourSurfaceTextBox.Select(position, 0);
To get the touch locations you can use
ReadOnlyTouchPointCollection touches = touchTarget.GetState();
Then you'll have to figure out where in relation to an object the touch was, but I this question is beyond the scope of the original question.
Have fun!

Categories