dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.24k stars 1.76k forks source link

Shell TitleView disappearing on tab change #9687

Closed belmonmi closed 1 year ago

belmonmi commented 2 years ago

Description

On iOS Simulator with Shell.FlyoutBehavior="Flyout" and Tabs, Shell.TitleView is disappearing on tab change.

Steps to Reproduce

  1. Create new .Net MAUI project.

  2. Add couple ContentPages.

  3. Add "AppTitleView" ContentView

  4. Edit AppShell.xaml to set Shell.FlyoutBehavior="Flyout"

  5. Create couple FlyoutItem with Tab elements.

  6. Add `

    ` to Content Pages
  7. Run application, switch from "Tab 1" to "Tab 2"

  8. Pages on the "Tab 1" will not show "Shell.TitleView", but if you named that TitleView control it still accesable from code behind.

  9. Simple project to demostrate the behavior: https://github.com/belmonmi/ShellTitleView

Version with bug

6.0.408

Last version that worked well

Unknown/Other

Affected platforms

iOS, I was not able test on other platforms

Affected platform versions

iOS 15.5

Did you find any workaround?

No

Relevant log output

2022-08-26 12:20:38.115611-0500 ShellTitleView[10063:986797] 
Options:
2022-08-26 12:20:38.115788-0500 ShellTitleView[10063:986797]   --bool-flag (Example)
    type: bool  default: false
2022-08-26 12:20:38.115883-0500 ShellTitleView[10063:986797]   --aot-lazy-assembly-load (Load assemblies referenced by AOT images lazily)
    type: bool  default: false
Resolved pending breakpoint for 'ShellTitleView.Program.Main(System.String[])' to C:\Projects\iNet6\Mobile\Test\Issues\ShellTitleView\ShellTitleView\Platforms\iOS\Program.cs:10 [0x00000].
Resolved pending breakpoint for 'Xamarin.HotReload.HotReloadAgent.BreakpointSendToIde(System.String)' to D:\a\_work\1\s\HotReload\Source\Xamarin.HotReload.Agent\HotReloadAgent.cs:419 [0x00000].
Resolved pending breakpoint for 'Xamarin.HotReload.HotReloadAgent.BreakpointCheckpoint()' to D:\a\_work\1\s\HotReload\Source\Xamarin.HotReload.Agent\HotReloadAgent.cs:414 [0x00000].
2022-08-26 12:20:40.312306-0500 ShellTitleView[10063:986797] SecTaskLoadEntitlements failed error=22 cs_flags=200, pid=10063
2022-08-26 12:20:40.312590-0500 ShellTitleView[10063:986797] SecTaskCopyDebugDescription: ShellTitleView[10063]/0#-1 LF=0
2022-08-26 12:20:40.319471-0500 ShellTitleView[10063:986797] SecTaskLoadEntitlements failed error=22 cs_flags=200, pid=10063
2022-08-26 12:20:40.319731-0500 ShellTitleView[10063:986797] SecTaskCopyDebugDescription: ShellTitleView[10063]/0#-1 LF=0
Thread started:  #2
Thread started:  #3
Thread started:  #4
2022-08-26 12:20:41.484090-0500 ShellTitleView[10063:986797] [TableView] Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view bounds, trait collection, layout margins, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes. Make a symbolic breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the debugger and see what caused this to occur, so you can avoid this action altogether if possible, or defer it until the table view has been added to a window. Table view: <_UIMoreListTableView: 0x7fa4daef2c00; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600002e14330>; layer = <CALayer: 0x600002415140>; contentOffset: {0, 0}; contentSize: {0, 0}; a
djustedContentInset: {0, 0, 0, 0}; dataSource: <UIMoreListController: 0x7fa4d9fa97c0>>
Thread started: .NET Timers #5
Thread started: <Thread Pool> #6
Thread started: .NET ThreadPool Gate #7
Thread started: <Thread Pool> #8
Thread started: <Thread Pool> #9
Thread started: <Thread Pool> #10
Thread started: <Thread Pool> #11
Thread started: <Thread Pool> #12
Thread started: <Thread Pool> #13
Thread started: <Thread Pool> #14
2022-08-26 12:20:45.960535-0500 ShellTitleView[10063:986829] Warning: observer object was not disposed manually with Dispose()
2022-08-26 12:20:45.960790-0500 ShellTitleView[10063:986829] Warning: observer object was not disposed manually with Dispose()
belmonmi commented 2 years ago

ShellTitle "Tab 1" selected - Shell Title is visible, "Tab 2" selected - Shell Title is visible, back to "Tab 1" - Shell Title disappeared.

avavricek commented 2 years ago

I had something similar where I had a Route assigned to a TabBar, but not the child tabs. Added those routes and it fixed the header title updating. It's refreshes slow if adding a new page to a stack, but it happens.

<TabBar Route="home">
       <Tab Title="Cats" Route="cats">
           <ShellContent Title="Cats" ContentTemplate="{DataTemplate local:MainPage}" />
       </Tab>
       <Tab Title="Dogs" Route="dogs">
           <ShellContent Title="Dogs" ContentTemplate="{DataTemplate local:MainPage}" />
       </Tab>
    </TabBar>
ghost commented 2 years ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

nebula2 commented 2 years ago

Is there a workaround for this? I have an image in titleview and if I read my logs correctly, the image simply gets disposed and is therefore not visible anymore.

this is pretty much a showstopper :/

belmonmi commented 2 years ago

@nebula2 Exactly! We cannot release as well until it fixed. Application title area has a vital information that has be visible always. BTW: works on Android, no issues. Very frustrated...

nebula2 commented 2 years ago

@belmonmi

Exactly! We cannot release as well until it fixed. Application title area has a vital information that has be visible always. BTW: works on Android, no issues. Very frustrated...

Jeah i started with designing android . yesterday I got an iPhone to test how the app looks and it was horrible. Spent the whole day fixing the most basic things. I have a readme with a list of issues which is getting bigger and bigger.

There are new issues which may be related to this one here. At least they sound like it.

10128

and

10350

It's terrible that I cannot even change to something else on iOS (like showing simple text, or an icon or whatever) because no matter what you put into there - it will disappear or it will be outdated ( #6582 ).

and seeing issues with "priority high" and "moved to backlog" has it's very own taste :-| Having a very hard time to stay calm >.<

belmonmi commented 2 years ago

@nebula2 "Having a very hard time to stay calm" - LOL. Your endurance is exceptional!! :)

nebula2 commented 2 years ago

@belmonmi I worked around it by not using shell at all. I replaced the shell by a combination of NavigationPage and Syncfusion's TabView.

There are disadvantages and I had to rewrite a few parts of my app in order to get this working - but at least my journey can go on.

belmonmi commented 2 years ago

@nebula2 thank you for the update. We are using Developer Express components; I will check if I can accomplish the same with what they provide.

nebula2 commented 2 years ago

@belmonmi feel free to contact me if you struggle. A sorrow shared is a sorrow halved

tdeborde2 commented 2 years ago

@belmonmi, @nebula2 I just added a workaround here, then remembered this issue. Looks like you might have found a workaround using some Syncfusion components. Unfortunately I was not able to use Syncfusion.

I tested my workaround using the flyout and tabs and it appears to work. It is essentially the same except you would need to add a menu item to your TitleView to present the flyout. I have added an example of how to do this in the repro project in #9269, which I believe is a duplicate.

nebula2 commented 2 years ago

@belmonmi, @nebula2 I just added a workaround here, then remembered this issue. Looks like you might have found a workaround using some Syncfusion components. Unfortunately I was not able to use Syncfusion.

I tested my workaround using the flyout and tabs and it appears to work. It is essentially the same except you would need to add a menu item to your TitleView to present the flyout. I have added an example of how to do this in the repro project in #9269, which I believe is a duplicate.

Thank you for sharing. Better than my approach. I wrote some fancy stuff that instantiates views instead of pages to render stuff inside the SfTabView, where every tab item is a view and not a page.

borrmann commented 2 years ago

attached video shows a Shell TitleView on an iOS device while navigating between Tabs using .NET 7. Sometimes the Title doesn't appear at all, sometimes it moves up, approximately half way out of the window, and sometimes it renders fine. The same TitleView works always as expected on android.

@rachelkang why is this in backlog? Can we expect a fix soon? It seems not possible to show anything else than a plain string in a Title on iOS using AppShell, which is the default template.

https://user-images.githubusercontent.com/20472867/200828892-3a5f3820-b8b2-4497-ba5c-1ad9dcad6d01.mov

vhugogarcia commented 2 years ago

Hello @belmonmi @borrmann ,

I fixed the issue by using a custom render for AppShell on iOS.

  1. Create a new class DemoMauiApp/Platforms/iOS/Renderers/CustomShellRenderer.cs
using CoreGraphics;
using Microsoft.Maui.Controls.Handlers.Compatibility;
using Microsoft.Maui.Controls.Platform.Compatibility;
using UIKit;

namespace DemoMauiApp.Platforms.iOS.Renderers;

public class CustomShellRenderer : ShellRenderer
{
    protected override IShellPageRendererTracker CreatePageRendererTracker()
    {
        return new CustomShellPageRendererTracker(this);
    }

    protected override IShellNavBarAppearanceTracker CreateNavBarAppearanceTracker()
    {
        return new NoLineAppearanceTracker();
    }
}

public class CustomShellPageRendererTracker : ShellPageRendererTracker
{
    public CustomShellPageRendererTracker(IShellContext context) : base(context) { }

    protected override void UpdateTitleView()
    {
        if (ViewController == null || ViewController.NavigationItem == null)
        {
            return;
        }

        var titleView = Shell.GetTitleView(Page);
        if (titleView == null)
        {
            var view = ViewController.NavigationItem.TitleView;
            ViewController.NavigationItem.TitleView = null;
            view?.Dispose();
        }
        else
        {
            var view = new CustomTitleViewContainer(titleView);
            ViewController.NavigationItem.TitleView = view;
        }
    }
}

public class CustomTitleViewContainer : UIContainerView
{
    public CustomTitleViewContainer(View view) : base(view)
    {
        TranslatesAutoresizingMaskIntoConstraints = false;
    }

    public override CGSize IntrinsicContentSize => UILayoutFittingExpandedSize;
}

public class NoLineAppearanceTracker : IShellNavBarAppearanceTracker
{
    public void Dispose() { }

    public void ResetAppearance(UINavigationController controller) { }

    public void SetAppearance(UINavigationController controller, ShellAppearance appearance)
    {
        var navBar = controller.NavigationBar;
        var navigationBarAppearance = new UINavigationBarAppearance();
        navigationBarAppearance.ConfigureWithOpaqueBackground();
        navigationBarAppearance.ShadowColor = UIColor.Clear;
        navigationBarAppearance.BackgroundColor = UIColor.White; // Set the background color you want on the Shell NavBar
        navBar.ScrollEdgeAppearance = navBar.StandardAppearance = navigationBarAppearance;
    }

    public void SetHasShadow(UINavigationController controller, bool hasShadow) { }

    public void UpdateLayout(UINavigationController controller) { }
}
  1. On the MauiProgram.cs configure the handler:

    var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureMauiHandlers(handlers =>
            {
    #if IOS
                handlers.AddHandler(typeof(Shell), typeof(DemoMauiApp.Platforms.iOS.Renderers.CustomShellRenderer));
    #endif
            });
  2. Rebuild and Run 💯

I hope this helps others as well! Thank you

borrmann commented 2 years ago

Thank you, works like a charm! Just had to use my inherited class from Shell, in my case AppShell in MauiProgram.cs

Also I would suggest to set the Backgroundcolor using a saved status, so stuff like AppThemeBinding should still work

    public void SetAppearance(UINavigationController controller, ShellAppearance appearance)
    {
        var navBar = controller.NavigationBar;
        var col = navBar.BackgroundColor;
        var navigationBarAppearance = new UINavigationBarAppearance();
        navigationBarAppearance.ConfigureWithOpaqueBackground();
        navigationBarAppearance.ShadowColor = UIColor.Clear;
        navigationBarAppearance.BackgroundColor = col; 
        navBar.ScrollEdgeAppearance = navBar.StandardAppearance = navigationBarAppearance;
    }
avavricek commented 1 year ago

Woot! That fix worked for us. Thanks!

belmonmi commented 1 year ago

@vhugogarcia , Thank you very much for the fix you posted. Very simple, elegant solution and the most important thing it is working.