András Tóth‘s professional blog
banditoth.net

Hey there 👋, I’m banditoth a .NET MAUI developer from Hungary.
I write about software development with .NET technologies.

You can find me on:
LinkedIn | Github | StackOverflow | X / Twitter | Threads

Tag: ios

  • Resizing the screen when an Entry gets focused in .NET MAUI

    When an entry field gets focused, the software keyboard appears, potentially covering the entry field. To provide a better user experience, you need to adjust the screen layout so that the entry field remains visible.

    First, create a parent class for your views, deriving from ContentPage, and add a HaveKeyboardOffsetProperty.

    public class ViewBase : ContentPage
    {
        public static readonly BindableProperty HasKeyboardOffsetProperty =
            BindableProperty.Create(nameof(HasKeyboardOffset), typeof(bool), typeof(ViewBase), false);
    
        public bool HasKeyboardOffset
        {
            get => (bool)GetValue(HasKeyboardOffsetProperty);
            set => SetValue(HasKeyboardOffsetProperty, value);
        }
    }
    
    

    iOS Solution:

    Next, create the PageElementMapper class to handle the keyboard appearance and adjust the screen layout.

    public class PageElementMapper
    {
        private static double _contentOriginalHeight;
        private static Thickness _contentOriginalMargin;
    
        public static void Map(IElementHandler handler, IElement view)
        {
            if (view is ViewBase viewData)
            {
                UIKeyboard.Notifications.ObserveWillShow((sender, args) =>
                {
                    if (viewData.HasKeyboardOffset)
                    {
                        _contentOriginalHeight = viewData.Content.Height;
                        _contentOriginalMargin = viewData.Content.Margin;
                        viewData.Content.HeightRequest = _contentOriginalHeight - args.FrameEnd.Height;
                        viewData.Content.Margin = new Thickness(0, args.FrameEnd.Height, 0, 0);
                    }
                });
    
                UIKeyboard.Notifications.ObserveWillHide((sender, args) =>
                {
                    if (viewData.HasKeyboardOffset)
                    {
                        viewData.Content.HeightRequest = _contentOriginalHeight;
                        viewData.Content.Margin = _contentOriginalMargin;
                    }
                });
            }
        }
    }
    
    

    Finally, register the mapper in the MauiProgram.cs file.

    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureMauiHandlers(handlers =>
                {
                    Microsoft.Maui.Handlers.PageHandler.ElementMapper.AppendToMapping("KeyboardOffset", (handler, view) =>
                    {
                        if (view is ViewBase)
                        {
    #if IOS
                            PageElementMapper.Map(handler, view);
    #endif
                        }
                    });
                });
    
            return builder.Build();
        }
    }
    
    

    Android solution:

    On Android, you can use a PropertyChanged method of the HasKeyboardOffset:

        private static void OnHasKeyboardOffsetPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
    #if ANDROID
            if (bindable is ViewBase view)
            {
                if (newValue is bool hasOffset)
                    if (hasOffset == true)
                        Microsoft.Maui.Controls.Application.Current.Dispatcher.Dispatch(() =>
                        {
                            Platform.CurrentActivity?.Window?.SetSoftInputMode(Android.Views.SoftInput.AdjustResize);
                        });
                    else
                        Microsoft.Maui.Controls.Application.Current.Dispatcher.Dispatch(() =>
                        {
                            Platform.CurrentActivity?.Window?.SetSoftInputMode(Android.Views.SoftInput.StateUnspecified);
                        });
            }
    #endif
        }
    

    Remarks

    The provided solution offers a way to manage this in .NET MAUI on iOS. However, always be open to improvements and share your solutions with the community for better practices. This might not be the best solution to do it.

  • .NET MAUI Hide software keyboard when tapping out of an Entry on iOS

    To hide the software keyboard when the user taps outside the entry field, set the HideSoftInputOnTapped property to True in your ContentPage definition.

    &lt;ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="MyMauiApp.MainPage"
                 HideSoftInputOnTapped="True">
    
        &lt;StackLayout Padding="10">
            &lt;Entry Placeholder="Tap here to enter text" />
        &lt;/StackLayout>
    &lt;/ContentPage>
    
    

    Please note that the HideSoftInputOnTapped property might not work as expected when tapping on certain controls like ScrollView. In such cases, you might need to implement a custom behavior to handle keyboard dismissal.

  • Using Different Entitlements for Debug and Release Modes in .NET MAUI – iOS

    When developing a mobile app using .NET MAUI, particularly for iOS, it’s essential to configure your application differently for debug and release modes. One of these differences is the APS-environment setting, which dictates how your app communicates with Apple Push Notification services (APNs) during development and production.

    What is Entitlements.plist?

    The Entitlements.plist is a property list (plist) file that defines various capabilities or entitlements for your app. Entitlements are special permissions that allow your app to use certain services provided by iOS, such as iCloud, In-App Purchases, or push notifications.

    For push notifications, the Entitlements.plist file contains the APS-environment key, which indicates to Apple whether your app is in development or production mode. Based on this, the app uses either the sandbox or production APNs.

    What is APS-environment?

    The APS-environment (Apple Push Services environment) is an entitlement used to specify the environment for push notifications. This entitlement informs Apple’s servers whether the app is running in a development environment or in production, determining which server to send the notifications through:

    • Development APS-environment: Used for testing push notifications during the app’s development phase. These notifications go through Apple’s sandbox APNs server.
    • Production APS-environment: Used for apps that have been published and distributed through the App Store. Notifications go through Apple’s production APNs server.

    This configuration helps separate testing from live user interactions and avoids accidental notification delivery to users during testing.

    Configuring Different APS-environments for Debug and Release

    To configure different environments for Debug and Release modes in your .NET MAUI project, you can modify your .csproj file as follows:

    &lt;PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-ios|AnyCPU'">
      &lt;CodesignEntitlements>Platforms\iOS\Entitlements.plist&lt;/CodesignEntitlements>
    &lt;/PropertyGroup>
    
    &lt;PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-ios|AnyCPU'">
      &lt;CodesignEntitlements>Platforms\iOS\Entitlements-Production.plist&lt;/CodesignEntitlements>
    &lt;/PropertyGroup>
    
    

    It’s important to ensure that both Entitlements.plist and Entitlements-Production.plist files are not included in the build by accident. This can be achieved by setting their Build Action to None:

    1. Right-click on each file (Entitlements.plist and Entitlements-Production.plist) in Visual Studio.
    2. Select Properties.
    3. Set the Build Action to None.

    This step ensures that the files are correctly associated with your app for code-signing purposes but are not compiled into the app bundle unnecessarily.

    (Update) Or you can simply use this approach in your .csproj file which is far more easier:

    	&lt;ItemGroup Condition="$(TargetFramework.Contains('-ios'))">
    		&lt;CustomEntitlements Include="aps-environment" Condition="'$(Configuration)' == 'Release'" Type="String" Value="production" />
    		&lt;CustomEntitlements Include="aps-environment" Condition="'$(Configuration)' == 'Debug'" Type="String" Value="development" />
    	&lt;/ItemGroup>
    
  • Azure Pipelines error after macOS / iOS update when building iOS apps with .NET MAUI

    If you are facing problems after a new version of macOS, or iOS have been released, stay tuned.

    In this example, I’m having issues with the new update of iOS 18 and macOS Sequioa. Some days or weeks are needed from Microsoft side to have a vmPool in the cloud to build your iOS apps with the latest SDKs, so theres a possibility that you might encounter this problem on some fresher updates aswell.

    So how the error looks like?

    ILLINK : error MT2362: The linker step 'ClassHandleRewriter' failed during processing: One or more errors occurred. (The type 'MapKit.MKSelectionAccessory' (used as a return type in MapKit.MKMapView/_MKMapViewDelegate.GetSelectionAccessory) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode). 
      		) (The type 'UIKit.UITextFormattingViewController' (used as a parameter in UIKit.UITextView/_UITextViewDelegate.DidBeginFormatting) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITextFormattingViewController' (used as a parameter in UIKit.UITextView/_UITextViewDelegate.DidEndFormatting) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITextFormattingViewController' (used as a parameter in UIKit.UITextView/_UITextViewDelegate.WillBeginFormatting) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITextFormattingViewController' (used as a parameter in UIKit.UITextView/_UITextViewDelegate.WillEndFormatting) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITab' (used as a parameter in UIKit.UITabBarController/_UITabBarControllerDelegate.AcceptItemsFromDropSession) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITab' (used as a parameter in UIKit.UITabBarController/_UITabBarControllerDelegate.DidSelectTab) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITab' (used as a parameter in UIKit.UITabBarController/_UITabBarControllerDelegate.DidSelectTab) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITabGroup' (used as a parameter in UIKit.UITabBarController/_UITabBarControllerDelegate.DisplayOrderDidChangeForGroup) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITab' (used as a parameter in UIKit.UITabBarController/_UITabBarControllerDelegate.GetDisplayedViewControllers) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITab' (used as a parameter in UIKit.UITabBarController/_UITabBarControllerDelegate.GetOperationForAcceptingItemsFromDropSession) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITab' (used as a parameter in UIKit.UITabBarController/_UITabBarControllerDelegate.ShouldSelectTab) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		) (The type 'UIKit.UITab[]' (used as a parameter in UIKit.UITabBarController/_UITabBarControllerDelegate.VisibilityDidChangeForTabs) is not available in iOS 17.2 (it was introduced in iOS 18.0). Please build with a newer iOS SDK (usually done by using the most recent version of Xcode).
      		)
    

    Solution after the proper vmImages are released by Microsoft:

    To resolve this issue, you need to update the macOS image used in your Azure Pipelines to the latest version. This ensures that the build environment uses the most recent version of Xcode, which includes the necessary iOS SDKs.

    Here’s how you can update your Azure Pipelines configuration:

    • Open your Azure Pipelines YAML file: Locate the YAML file that defines your pipeline configuration.
    • Update the vmImage: Change the vmImage to macOS-latest to ensure that the latest macOS version is used, which includes the most recent Xcode and iOS SDKs.
    pool:
      vmImage: 'macOS-latest'
    
    • Save and commit the changes: Save the updated YAML file and commit the changes to your repository.
    • Run the pipeline: Trigger a new build in Azure Pipelines to verify that the issue is resolved.

    By updating the vmImage to macOS-latest, you ensure that your build environment is using the latest tools and SDKs, which should resolve the linker errors related to unavailable types.

    Solution until the proper vmImages are not present:

    Modify your pipeline, and don’t forget to add comments on the modified pipeline!

    • Open your Azure Pipelines YAML file: Locate the YAML file that defines your pipeline configuration.
    • Update the YAML file:
    pool: 
         vmImage: 'macOS-14'
    # András @ 24.10.04: Explicit version needed until macos15 vmimage is being released. Use latest when possible
        steps:
          - task: CmdLine@2
            displayName: 'Select XCode 16 explicitly'
            inputs:
              script: 'sudo xcode-select -s /Applications/Xcode_16.app'
    # András @ 24.10.04: Explicit version code needed for XCode. Remove this task when possible
    
          - task: CmdLine@2
            displayName: 'Install MAUI Workload'
            inputs:
              script: 'dotnet workload install maui --version 8.0.402'
    # András @ 24.10.04: Explicit version needed until macos15 vmimage is being released. Use no --version tag if possible
    
    • Save and commit the changes: Save the updated YAML file and commit the changes to your repository.
    • Run the pipeline: Trigger a new build in Azure Pipelines to verify that the issue is resolved.
  • .NET MAUI iOS – Can’t launch the app, crashes on splash screen error

    This content has 10 months. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

    If you are facing issues launching your iOS application in VS Code, then try to analyse the Debug Console of your application.
    If you see things like this:

    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/Newtonsoft.Json.dll
    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/System.Reflection.Emit.Lightweight.dll
    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/System.Reflection.Emit.ILGeneration.dll
    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/System.Reflection.Primitives.dll
    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/System.Data.Common.dll
    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/Syncfusion.Maui.Inputs.dll
    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/ZF.Packages.Maui.Fonts.dll
    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/ZF.Packages.Maui.Views.Alerts.dll
    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/Refit.HttpClientFactory.dll
    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/Microsoft.Extensions.Http.dll
    Loaded assembly: /private/var/containers/Bundle/Application/50529075-A03E-45EC-99F6-0A539036E226/app.app/MetroLog.Maui.dll
    

    And then your application suddenly crashes showing the splashscreen, then the debugger might be slow.

    XCode 15 have problems with debugging. If you have “Connect via network” enabled on your XCode settings under Devices and Simulators, than this might be the problem. Even if you are connecting your phone wired, it starts the application debugging via wifi.
    Try updating XCode to 15.4, and update your OS to macOS Sonoma 15.4 aswell

    You can countercheck this problem by disabling the wifi on your physical device. In this case the app installs, but crashes instantly when trying to launch it.

    Additionally in Visual Studio code the solution you can disable some settings in the C# extension’s settings the Debug > Logging to speed up the loading progress when launching your app.