Search code examples
c#iosviewmaui

Error changing view in .net Maui iOS ObjCRuntime.ObjCException


I am trying to create a kind of form with several questions, the thing is that in each view there is a question and when moving to the next one, the type of question may or may not change, when my question type is the same as the next one I launch the same view, in Android it allows but in iOS I get the following problem:

ObjCRuntime.ObjCException
  Message = Objective-C exception thrown.  Name: NSInvalidArgumentException Reason: <Microsoft_Maui_Controls_Platform_Compatibility_ShellSectionRenderer: 0x116f56800> is pushing the same view controller instance (<Microsoft_Maui_Platform_PageViewController: 0x117869db0>) more than once which is not supported and is most likely an error in the application : com.geicam.asociados
Native stack trace:
    0   CoreFoundation                      0x00000001804ae0f8 __exceptionPreprocess + 172
    1   libobjc.A.dylib                     0x0000000180087db4 objc_exception_throw + 56
    2   UIKitCore                           0x0000000184bf9334 -[UINavigationController pushViewController:transition:forceImmediate:] + 3736
    3   UIKitCore                           0x0000000184bf832c -[UINavigationController pushViewController:animated:] + 756
    4   libxamarin-dotnet-debug.dylib       0x0000000107f94bd4 xamarin_dyn_objc_msgSendSuper + 164
    5   geicam.asociados                    0x000000010589d95c interp_to_native_trampoline + 156
    6   libmonosgen-2.0.dylib               0x00000001086ff6f8 ves_pinvoke_method + 584
    7   libmonosgen-2.0.dylib               0x00000001086f3e84 mono_interp_exec_method + 3052
    8   libmonosgen-2.0.dylib               0x00000001086f194c interp_runtime_invoke + 244
    9   libmonosgen-2.0.dylib               0x000000010860abf0 mono_jit_runtime_invoke + 1116
    10  libmonosgen-2.0.dylib               0x00000001087c6cb0 mono_runtime_try_invoke + 156
    11  libmonosgen-2.0.dylib               0x00000001087c9d28 mono_runtime_invoke + 488
    12  geicam.asociados                    0x00000001058bf400 _ZL30native_to_managed_trampoline_9P11objc_objectP13objc_selectorPP11_MonoMethodj + 316
    13  geicam.asociados                    0x00000001059103bc -[UIKit_UIControlEventProxy BridgeSelector] + 44
    14  UIKitCore                           0x00000001853d58ec -[UIApplication sendAction:to:from:forEvent:] + 96
    15  UIKitCore                           0x0000000184ce23d0 -[UIControl sendAction:to:forEvent:] + 108
    16  UIKitCore                           0x0000000184ce2714 -[UIControl _sendActionsForEvents:withEvent:] + 268
    17  UIKitCore                           0x0000000184cdf188 -[UIButton _sendActionsForEvents:withEvent:] + 120
    18  UIKitCore                           0x0000000184ce13ac -[UIControl touchesEnded:withEvent:] + 392
    19  UIKitCore                           0x0000000185409354 -[UIWindow _sendTouchesForEvent:] + 900
    20  UIKitCore                           0x000000018540a878 -[UIWindow sendEvent:] + 3104
    21  UIKitCore                           0x00000001853ea238 -[UIApplication sendEvent:] + 580
    22  UIKitCore                           0x000000018546dff4 __dispatchPreprocessedEventFromEventQueue + 1684
    23  UIKitCore                           0x0000000185470e90 __processEventQueue + 5604
    24  UIKitCore                           0x00000001854697f0 __eventFetcherSourceCallback + 160
    25  CoreFoundation                      0x000000018040ee48 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
    26  CoreFoundation                      0x000000018040ed90 __CFRunLoopDoSource0 + 172
    27  CoreFoundation                      0x000000018040e500 __CFRunLoopDoSources0 + 232
    28  CoreFoundation                      0x0000000180408be8 __CFRunLoopRun + 768
    29  CoreFoundation                      0x00000001804084d4 CFRunLoopRunSpecific + 572
    30  GraphicsServices                    0x000000018ef2aae4 GSEventRunModal + 160
    31  UIKitCore                           0x00000001853d0a28 -[UIApplication _run] + 868
    32  UIKitCore                           0x00000001853d46b0 UIApplicationMain + 124
    33  libxamarin-dotnet-debug.dylib       0x0000000107f4fa84 xamarin_UIApplicationMain + 60
    34  libmonosgen-2.0.dylib               0x0000000108700abc do_icall + 316
    35  libmonosgen-2.0.dylib               0x00000001086ff35c do_icall_wrapper + 356
    36  libmonosgen-2.0.dylib               0x00000001086f3dac mono_interp_exec_method + 2836
    37  libmonosgen-2.0.dylib               0x00000001086f194c interp_runtime_invoke + 244
    38  libmonosgen-2.0.dylib               0x000000010860abf0 mono_jit_runtime_invoke + 1116
    39  libmonosgen-2.0.dylib               0x00000001087c5c9c mono_runtime_invoke_checked + 148
    40  libmonosgen-2.0.dylib               0x00000001087cd27c mono_runtime_exec_main_checked + 116
    41  libmonosgen-2.0.dylib               0x000000010865ee78 mono_jit_exec + 364
    42  libxamarin-dotnet-debug.dylib       0x0000000107f93a38 xamarin_main + 2320
    43  geicam.asociados                    0x0000000105981560 main + 72
    44  dyld                                0x000000010654d544 start_sim + 20
    45  ???                                 0x0000000106652154 0x0 + 4402258260
    46  ???                                 0xb162800000000000 0x0 + 12781919429919244288

  Origin = Microsoft.iOS

Whenever you press the next button, it does the following function:

public async void CargarView(int id)
{

    switch ((enumFormTipoPregunta)id)
    {
        case enumFormTipoPregunta.TextoCorto:
            var navigationTextoCorto = new Dictionary<string, object>
            {
                 { "Formulario", formul },
                { "Contador", cfo },
                { "Respuesta", gRespu }
            };
            await Shell.Current.GoToAsync($"{nameof(PreguntaTCortoView)}", navigationTextoCorto);
            break;
        case enumFormTipoPregunta.Parrafo:
            var navigationParrafo = new Dictionary<string, object>
            {
                 { "Formulario", formul },
                { "Contador", cfo },
                { "Respuesta", gRespu }
            };
            await Shell.Current.GoToAsync($"{nameof(PreguntaTLargoView)}", navigationParrafo);
            break;
        case enumFormTipoPregunta.Opcion:
            var navigationParameter = new Dictionary<string, object>
            {
                 { "Formulario", formul },
                { "Contador", cfo },
                { "Respuesta", gRespu }
            };
            await Shell.Current.GoToAsync($"{nameof(PreguntaOpcionView)}", navigationParameter);
            break;
        case enumFormTipoPregunta.Multiple:
            var navigationFormulario = new Dictionary<string, object>
            {
                { "Formulario", formul },
                { "Contador", cfo },
                { "Respuesta", gRespu }
            };
            await Shell.Current.GoToAsync($"{nameof(PreguntaMultipleView)}", navigationFormulario);
            break;
        case enumFormTipoPregunta.Desplegable:
            var navigationDesplegable = new Dictionary<string, object>
            {
                { "Formulario", formul },
                { "Contador", cfo },
                { "Respuesta", gRespu }
            };
            await Shell.Current.GoToAsync($"{nameof(PreguntaOpcionView)}", navigationDesplegable);
            break;
        case enumFormTipoPregunta.Multiple2Col:
            var navigationMultiple2Col = new Dictionary<string, object>
            {
                { "Formulario", formul },
                { "Contador", cfo },
                { "Respuesta", gRespu }
            };
            await Shell.Current.GoToAsync($"{nameof(PreguntaMultipleView)}", navigationMultiple2Col);
            break;
        case enumFormTipoPregunta.Numerico:
            var navigationNumerico = new Dictionary<string, object>
            {
                { "Formulario", formul },
                { "Contador", cfo },
                { "Respuesta", gRespu }
            };
            await Shell.Current.GoToAsync($"{nameof(PreguntaNumericaView)}", navigationNumerico);
            break;
        case enumFormTipoPregunta.Valoracion:
            var navigationValoracion = new Dictionary<string, object>
            {
                { "Formulario", formul },
                { "Contador", cfo },
                { "Respuesta", gRespu }
            };
            await Shell.Current.GoToAsync($"{nameof(PreguntaValoracionView)}", navigationValoracion);
            break;
        case enumFormTipoPregunta.Fecha:
            var navigationFecha = new Dictionary<string, object>
            {
                { "Formulario", formul },
                { "Contador", cfo },
                { "Respuesta", gRespu }
            };
            await Shell.Current.GoToAsync($"{nameof(PreguntaFechaView)}", navigationFecha);
            break;
        default:
            break;
    }
}

I've been doing some research and the problem is that iOS doesn't allow you to launch the same view. so any idea how I could change it so that it works on both devices?


Solution

  • You can use DataTemplateSelector to change view:

    Create a model

    namespace MauiApp2.Model
    {
        public class Question
        {
            public string? QuestionText { get; set; }
            public string? QuestionType { get; set; } // e.g., "Text", "MultipleChoice", etc.
            public List<string>? Options { get; set; } // For multiple choice questions
        }
    }
    

    Then viewmodel

    namespace MauiApp2.VieModel
    {
        public class QuestionsViewModel : INotifyPropertyChanged
        {
            public ObservableCollection<Question> Questions { get; set; }
            public QuestionsViewModel()
            {
                CreateQuestions();
            }
     
            void CreateQuestions()
            {
                Questions = new ObservableCollection<Question>
                {
                    new Question() { QuestionText = "What's your name?", QuestionType = "Text" },
                    new Question() { QuestionText = "Your hobbies", QuestionType = "MultipleChoice", Options=new List<string>(){"Game", "Photography", "Cycling" } },
                    new Question() { QuestionText = "How old are you?", QuestionType = "Text" },
                    new Question() { QuestionText = "Your favourite foods ", QuestionType = "MultipleChoice",Options=new List<string>(){"Beef", "Hamburger", "Salad" } }
                };
            }
     
            public event PropertyChangedEventHandler PropertyChanged;
     
            protected void OnPropertyChanged(string propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    Create a DataTemplateSelector

    namespace MauiApp2
    {
        public class QuestionTemplateSelector : DataTemplateSelector
        {
            public DataTemplate TextQuestionTemplate { get; set; }
            public DataTemplate MultipleChoiceQuestionTemplate { get; set; }
     
            protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
            {
                var question = item as Question;
                switch (question?.QuestionType)
                {
                    case "Text":
                        return TextQuestionTemplate;
     
                    case "MultipleChoice":
                        return MultipleChoiceQuestionTemplate;
     
                    default:
                        return null;
                }
            }
        }
    }
    

    Use them in MainPage

    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:vm="clr-namespace:MauiApp2.VieModel"
                 xmlns:local="clr-namespace:MauiApp2"
                 x:Class="MauiApp2.MainPage">
       <ContentPage.BindingContext>
           <vm:QuestionsViewModel/>
       </ContentPage.BindingContext>
       <ContentPage.Resources>
           <DataTemplate x:Key="textQuestionTemplate">
               <StackLayout>
                   <Frame HasShadow="True"
                           BorderColor="DarkGray"
                           CornerRadius="5"
                           Margin="20"
                           HeightRequest="300"
                           HorizontalOptions="Center"
                           VerticalOptions="CenterAndExpand">
                       <StackLayout>
                           <Label Text="{Binding QuestionText}" />
                           <Entry Placeholder="Enter your answer here" />
                       </StackLayout>
                   </Frame>
               </StackLayout>
           </DataTemplate>
           <DataTemplate x:Key="multipleChoiceQuestionTemplate">
               <StackLayout>
                   <Frame HasShadow="True"
                           BorderColor="DarkGray"
                           CornerRadius="5"
                           Margin="20"
                           HeightRequest="300"
                           HorizontalOptions="Center"
                           VerticalOptions="CenterAndExpand">
                       <StackLayout>
                           <Label Text="{Binding QuestionText}" />
                           <ListView ItemsSource="{Binding Options}" />
                       </StackLayout>
                   </Frame>
               </StackLayout>
           </DataTemplate>
           <local:QuestionTemplateSelector x:Key="questionTemplateSelector"
                                                TextQuestionTemplate="{StaticResource textQuestionTemplate}"
                                                MultipleChoiceQuestionTemplate="{StaticResource multipleChoiceQuestionTemplate}"/>
       </ContentPage.Resources>
       <StackLayout Margin="20">
           <CarouselView ItemsSource="{Binding Questions}"
                          IndicatorView="indicatorView"
                          ItemTemplate="{StaticResource questionTemplateSelector}" />
           <IndicatorView x:Name="indicatorView"
                           IndicatorColor="LightGray"
                           SelectedIndicatorColor="DarkGray"
                           HorizontalOptions="Center" />
       </StackLayout>
    </ContentPage>
    

    Hope it can help you!