Open Closed

.Net Maui App Bugs, Especially on ios #6236


User avatar
0
cangunaydin created
  • ABP Framework version: v7.40
  • UI Type: Angular with maui
  • Database System: EF Core PostgreSql
  • Tiered (for MVC) or Auth Server Separated (for Angular): yes

Hello when you create a new template with maui. Android app looks ok and working fine. Still some problems on the shell menu with visibility but overall it is okay. But when i try to run the app on ios with physical device or simulator. it looks very bad. here are some screen shots so you can understand what i mean.

dark theme menu: avatar is not displayed good paddings and margins are weird.

dark theme tenants list: when it is empty empty image is overflowing.

dark theme user list: action list not working, empty image problem list is not looking ok.

settings page profile pictures. everything is looking weird.

light theme: search box is black

light theme menu: avatar problem

light theme: all the inputs are looking weird.

the question here is, is this related with maui or theming problem?

Also i realized that when you try to run the app on windows, shell flyout menu have some problems, sometimes menu items are not showing when you sign in and out. Sometimes their order has been mixed. So signout is coming before tenants. I think list is not refreshed so it keeps the buttons as it is.

What can be done to solve these problems? With v8 are the problems gonna be fixed or should i fix everything manually one by one for my project?


4 Answer(s)
  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    Hello,

    Thanks for the feedback, we had some workarounds, they're probably not necessary anymore, I'll take a look and inform you

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    We've found some problems and solved it, they'll be included in the next release but I'll share you workaround that you can apply by your own.

    Firstly Create the following Handlers to remove visual effects of inputs:

    using Microsoft.Maui.Handlers;
    using Microsoft.Maui.Platform;
    
    #if ANDROID
    using Android.Graphics.Drawables;
    using AndroidX.AppCompat.Widget;
    using Microsoft.Maui.Controls.Platform;
    using Microsoft.Maui.Controls.Compatibility.Platform.Android;
    #endif
    
    #if WINDOWS
    using Microsoft.UI.Xaml.Controls;
    using Windows.UI.Notifications;
    #endif
    
    namespace MyCompanyName.MyProjectName.Maui.Handlers;
    
    public partial class EntryVisualEffectsRemoverHandler : EntryHandler
    {
        public EntryVisualEffectsRemoverHandler()
        {
        }
    
        public EntryVisualEffectsRemoverHandler(IPropertyMapper mapper) : base(mapper)
        {
        }
    }
    
    #if ANDROID
    public partial class EntryVisualEffectsRemoverHandler : EntryHandler
    {
        protected override AppCompatEditText CreatePlatformView()
        {
            var nativeView = base.CreatePlatformView();
    
            using (var gradientDrawable = new GradientDrawable())
            {
                gradientDrawable.SetColor(global::Android.Graphics.Color.Transparent);
                nativeView.SetBackground(gradientDrawable);
                nativeView.BackgroundTintList = Android.Content.Res.ColorStateList.ValueOf(Colors.Transparent.ToAndroid());
            }
    
            return nativeView;
        }
    }
    #endif
    
    #if IOS || MACCATALYST
    public partial class EntryVisualEffectsRemoverHandler : EntryHandler
    {
        protected override MauiTextField CreatePlatformView()
        {
            var nativeView = base.CreatePlatformView();
    
            nativeView.BorderStyle = UIKit.UITextBorderStyle.None;
    
            return nativeView;
        }
    }
    #endif
    
    #if WINDOWS
    public partial class EntryVisualEffectsRemoverHandler : EntryHandler
    {
        protected override TextBox CreatePlatformView()
        {
            var nativeView = base.CreatePlatformView();
    
            nativeView.BorderThickness = new Microsoft.UI.Xaml.Thickness(0);
            nativeView.Style = null;
            return nativeView;
        }
    }
    #endif
    
    using Microsoft.Maui.Handlers;
    using Microsoft.Maui.Platform;
    
    #if WINDOWS
    using Microsoft.UI.Xaml.Controls;
    #endif
    
    #if ANDROID
    using Android.Graphics.Drawables;
    using Microsoft.Maui.Controls.Platform;
    using Microsoft.Maui.Controls.Compatibility.Platform.Android;
    #endif
    
    #if IOS || MACCATALYST
    using UIKit;
    #endif
    
    namespace MyCompanyName.MyProjectName.Maui.Handlers;
    public partial class PickerVisualEffectsRemoverHandler : PickerHandler
    {
        public PickerVisualEffectsRemoverHandler()
        {
        }
    
        public PickerVisualEffectsRemoverHandler(IPropertyMapper mapper) : base(mapper)
        {
        }
    }
    
    #if ANDROID
    public partial class PickerVisualEffectsRemoverHandler : PickerHandler
    {
        protected override MauiPicker CreatePlatformView()
        {
            var nativeView = base.CreatePlatformView();
    
            using (var gradientDrawable = new GradientDrawable())
            {
                gradientDrawable.SetColor(global::Android.Graphics.Color.Transparent);
                nativeView.SetBackground(gradientDrawable);
                nativeView.BackgroundTintList = Android.Content.Res.ColorStateList.ValueOf(Colors.Transparent.ToAndroid());
            }
    
            return nativeView;
        }
    }
    #endif
    
    #if IOS || MACCATALYST
    public partial class PickerVisualEffectsRemoverHandler : PickerHandler
    {
        protected override MauiPicker CreatePlatformView()
        {
            var nativeView = base.CreatePlatformView();
    
            nativeView.BorderStyle = UITextBorderStyle.None;
    
            return nativeView;
        }
    }
    #endif
    
    #if WINDOWS
    public partial class PickerVisualEffectsRemoverHandler : PickerHandler
    {
        protected override ComboBox CreatePlatformView()
        {
            var nativeView = base.CreatePlatformView();
    
            nativeView.BorderThickness = new Microsoft.UI.Xaml.Thickness(0);
    
            return nativeView;
        }
    }
    #endif
    
    using Microsoft.Maui.Handlers;
    using Microsoft.Maui.Platform;
    
    #if ANDROID
    using Android.App;
    using Android.Graphics.Drawables;
    using Microsoft.Maui.Controls.Platform;
    using Microsoft.Maui.Controls.Compatibility.Platform.Android;
    #endif
    #if IOS || MACCATALYST
    using UIKit;
    #endif
    #if WINDOWS
    using Microsoft.UI.Xaml.Controls;
    #endif
    
    namespace MyCompanyName.MyProjectName.Maui.Handlers;
    public partial class DatePickerVisualEffectsRemoverHandler : DatePickerHandler
    {
        public DatePickerVisualEffectsRemoverHandler()
        {
        }
    
        public DatePickerVisualEffectsRemoverHandler(IPropertyMapper mapper) : base(mapper)
        {
        }
    }
    
    #if ANDROID
    public partial class DatePickerVisualEffectsRemoverHandler : DatePickerHandler
    {
        protected override MauiDatePicker CreatePlatformView()
        {
            var nativeView = base.CreatePlatformView();
    
            using (var gradientDrawable = new GradientDrawable())
            {
                gradientDrawable.SetColor(global::Android.Graphics.Color.Transparent);
                nativeView.SetBackground(gradientDrawable);
                nativeView.BackgroundTintList = Android.Content.Res.ColorStateList.ValueOf(Colors.Transparent.ToAndroid());
            }
    
            return nativeView;
        }
    }
    #endif
    
    #if IOS
    public partial class DatePickerVisualEffectsRemoverHandler : DatePickerHandler
    {
        protected override MauiDatePicker CreatePlatformView()
        {
            var nativeView = base.CreatePlatformView();
    
            nativeView.BorderStyle = UITextBorderStyle.None;
    
            return nativeView;
        }
    
    }
    #endif
    
    #if MACCATALYST
    public partial class DatePickerVisualEffectsRemoverHandler : DatePickerHandler
    {
        protected override UIDatePicker CreatePlatformView()
        {
            var nativeView = base.CreatePlatformView();
    
            nativeView.Alpha = 0f;
    
            return nativeView;
        }
    }
    #endif
    
    #if WINDOWS
    public partial class DatePickerVisualEffectsRemoverHandler : DatePickerHandler
    {
        protected override CalendarDatePicker CreatePlatformView()
        {
            var nativeView = base.CreatePlatformView();
            nativeView.BorderThickness = new Microsoft.UI.Xaml.Thickness(0);
            return nativeView;
        }
    }
    #endif
    
    • And Configure them in MauiProgram.cs
    builder.ConfigureMauiHandlers(handlers =>
    {
        handlers.AddHandler(typeof(Entry), typeof(EntryVisualEffectsRemoverHandler));
        handlers.AddHandler(typeof(Picker), typeof(PickerVisualEffectsRemoverHandler));
        handlers.AddHandler(typeof(DatePicker), typeof(DatePickerVisualEffectsRemoverHandler));
    });
    
    
    • It seems SearchBar is shown as Black always by MAUI, so you can you Entry instead Searchbar in your project.

    • Update Radio Button in the Profile Page like the following:

    <RadioButton Value="{x:Static account:ProfilePictureType.None}">
        <RadioButton.Content>
            <Label Text="{ext:Translate UseDefault}" VerticalOptions="Center" />
        </RadioButton.Content>
    </RadioButton>
    
    <RadioButton Value="{x:Static account:ProfilePictureType.Gravatar}">
        <RadioButton.Content>
            <Label Text="{ext:Translate UseGravatar}" VerticalOptions="Center" />
        </RadioButton.Content>
    </RadioButton>
    
    <RadioButton Value="{x:Static account:ProfilePictureType.Image}">
        <RadioButton.Content>
            <Label Text="{ext:Translate SelectNewImage}" VerticalOptions="Center" />
        </RadioButton.Content>
    </RadioButton>
    
    • Fix the EmptyView by updating like following
    <CollectionView.EmptyView>
        <Image Source="empty.png"
            MaximumWidthRequest="120"
            MaximumHeightRequest="120"
            VerticalOptions="Center"
            HorizontalOptions="Center"
            Opacity=".5"/>
    </CollectionView.EmptyView>
    
  • User Avatar
    0
    cangunaydin created

    Hello thanks for the feedback, I have also started to fix the things one by one.

    • problem with empty image on ios comes from refreshview. there was an open issue for it. https://github.com/dotnet/maui/issues/7315 i think they fix that with .net 8. to overcome the problem you can wrap the collectionview with grid sth like this. then you don't need to change the width request. everything works fine.
    • for the borders on entry i think your code is removing all the borders, however i like the android style with border bottom. IOS doesn't support that though as i understand. Maybe i need to remove the frames i am not so sure about it. I will find sth for it.
    • I couldn't find the radiobutton fix while i was trying different variations, so i was planning to switch to another component so it is nice that you share it with me i can use it.

    I have also upgraded my app to .net 8 yesterday, some things have been fixed however still some problems persist and some new problems arrived.

    I will write over here if i can fix those things that i mention

    Also worth to mention, I tried this .net 7, on list pages, maybe it has been fixed on .net 8 i didn't try

    <RefreshView Grid.Row="1" IsRefreshing="{Binding IsBusy}" Command="{Binding RefreshCommand}">

    this code makes the RefreshCommand triggers on the page load. since page appearing is also triggering the same code you call the endpoint twice. There was a discussion about it also in .net maui github issues. To overcome the situation, what i did was to bind RefreshCommand.IsRunning property instead of IsBusy. The reason behind this issue is when you call RefreshCommand it also sets the IsBusy property to true then it triggers the RefreshCommand again. new code is sth similar like this in my case.

    <RefreshView Grid.Row="1" IsRefreshing="{Binding RefreshCommand.IsRunning}" Command="{Binding RefreshCommand}">

  • User Avatar
    0
    cangunaydin created

    Hello again, new updates from me. Now i fixed everything that i wanted. It was little struggle though. I will write here what I have done.

    • As you mentioned on the previous post. you have added remove borders classes from all platforms for entry,picker and datepicker. On the top of that (cause what i want is to have a bottom border) i have added some styles to Styles.xaml.

    <Style TargetType="BoxView" x:Key="InputSeparator"> <Setter Property="Margin" Value="{OnPlatform Android='0,0,0,0',iOS='0,10,0,0',MacCatalyst='0,10,0,0'}"></Setter> <Setter Property="HeightRequest" Value="1"> </Setter> <Setter Property="HorizontalOptions" Value="Fill"></Setter> </Style>

    &lt;Style TargetType=&quot;Label&quot; x:Key=&quot;InputLabel&quot;&gt;
       &lt;Setter Property=&quot;Margin&quot; Value=&quot;{OnPlatform iOS=&#39;0,0,0,5&#39;,MacCatalyst=&#39;0,0,0,5&#39;}&quot;&gt;&lt;/Setter&gt;
    &lt;/Style&gt;
    

    Then i have added those styles to label and entries for form elements. As an example:

    as a result i got some border at the end for both android and ios.

    • Second issue was with checkbox on ios. there was no margin, so i added this style on the checkbox.
    • Third issue as i have mentioned before it was getting black textcolor when the edit page is requested on dark theme. This was an issue related with async void. For ex: on TenantEditModal, this was the method that has been called when edit has been clicked.
    async partial void OnIdChanged(string? value)
    

    This was causing threading problems i guess. So probably changing color was not invoked. so what i did instead is use AsyncHelper and leave the method as sync. Here is the code.

    partial void OnIdChanged(string? value)
        {
            IsBusy = true;
            AsyncHelper.RunSync(GetTenant);
            AsyncHelper.RunSync(GetEditions);
            IsBusy = false;
        }
    

    i have seen some people are using async void but according to me it is not the best practice.From my experience, it always bring some problems with it. The same problem exists on WeakReferenceMessenger. I have changed the code over there also. So i have been able to get the user interface that i wanted. here is how it looks at the end. Thanks for the code that has been provided by the way, it helped me a lot. On the other hand what i think is, on the long run adding RemovingHandlers to the project can be problematic in some cases. But for now it is good to go.

Made with ❤️ on ABP v9.1.0-preview. Updated on December 13, 2024, 06:09