Repository navigation
[Bug] SegmentedControl Invisible in Shell.TitleView on iOS 26 (Liquid Glass Rendering Issue) #38
Description
Activity
- changed the title
[-][Bug][/-][+][Bug] SegmentedControl Invisible in Shell.TitleView on iOS 26 (Liquid Glass Rendering Issue)[/+]on Oct 19, 2025 Thanks for reporting. I just tested it on my iPhone device with a net8.0-ios app and there it works fine. Are you using net9.0 and Xcode 26 to compile your app?
Hi, thanks for the quick reply!
Yes, that's correct. I can confirm that this issue is specific to the newer toolchain.- My Environment: .NET 9.0 with Xcode 26, targeting iOS 26.
The good news is that I've continued debugging and have found a stable workaround that solves the problem completely on my net9.0 setup, making the control work perfectly on both the iOS 26 simulator and physical devices.
The issue seems to be how the TitleView's container view is declared and initialized.
The Problematic Approach (Before - Fails on net9.0 / iOS 26)
My original code declared a specific StackLayout as a class property and initialized it in the page's constructor.
// In the class properties: private StackLayout PageTicketMode; // In the page constructor: public MyPage() { // ... PageTicketMode = new StackLayout { /* ... properties ... */ }; // ... }This pattern, which worked fine before, appears to cause critical layout measurement issues within the new UINavigationBar on iOS 26 when compiled with .NET 9.
The Stable Workaround (After - Works on net9.0 / iOS 26)
The solution is to declare the container as a more generic View and then instantiate the appropriate, platform-specific layout (Grid for iOS) "just-in-time" when it's actually needed.
- Change the property declaraton from StackLayout to the base View type:
// In the class properties: private View PageTicketMode; // Changed from StackLayout- Remove the container initialization from the constructor.
- Create the platform-specific container within the setup logic. For iOS, this involves creating the Grid that correctly handles measuremen
// Inside the platform-specific logic (e.g., a SetupTitleView method) else if (DeviceInfo.Platform == DevicePlatform.iOS) { // 1. Create a Grid specifically for iOS var iosContainer = new Grid { HeightRequest = 44 }; // 2. Create and configure the SegmentedControl var segmentedControl = new SegmentedControl(); // ... add children and properties ... // 3. Add the control to the Grid iosContainer.Children.Add(segmentedControl); // 4. Assign the newly created Grid to the property PageTicketMode = iosContainer; }By switching from a pre-initialized StackLayout to a "just-in-time" Grid assigned to a generic View property, the MAUI layout system now correctly measures and renders the control on physical devices with iOS 26.
Hope this detailed information helps you pinpoint the issue in the new environment! Let me know if you need any more details.Hi again,
Following up on my previous comments about the intermittent instability. I've pulled the source code for the SegmentedControlHandler.cs on iOS and I believe I've located the exact cause of the
NSInternalInconsistencyExceptionrace condition.The issue lies in the
UpdateSegmentedControlmethod. It uses a destructive "remove-and-rebuild" strategy:private static void UpdateSegmentedControl(...) { uiSegmentedControl.RemoveAllSegments(); // This line creates the race condition for (var i = 0; i < segmentedControl.Children.Count; i++) { uiSegmentedControl.InsertSegment(...); } // ... }This
RemoveAllSegments()call creates a small time window where the native UISegmentedControl is empty. On iOS 26, the OS seems to query the control's state more frequently during UI updates, and if it does so in this window, it finds 0 sections, causing the native crash. Could be caused from a massive use of animation liquid glass effect?A more robust approach would be to intelligently synchronize the state instead of completely rebuilding it. I've drafted a potential fix for the UpdateSegmentedControl method that avoids this issue by only rebuilding when the segment count changes:
// Suggested robust implementation private static void UpdateSegmentedControl(UISegmentedControl uiSegmentedControl, SegmentedControl segmentedControl) { // 1. Intelligently synchronize the number of segments if (uiSegmentedControl.NumberOfSegments != segmentedControl.Children.Count) { uiSegmentedControl.RemoveAllSegments(); for (var i = 0; i < segmentedControl.Children.Count; i++) { uiSegmentedControl.InsertSegment(segmentedControl.Children[i].Text, i, false); } } else { // 2. If segment count is the same, just update titles if they differ for (var i = 0; i < segmentedControl.Children.Count; i++) { if (uiSegmentedControl.GetTitle(i) != segmentedControl.Children[i].Text) { uiSegmentedControl.SetTitle(segmentedControl.Children[i].Text, i); } } } // ... The rest of the update logic remains the same ... }This "diffing" approach ensures the control is never empty during simple property updates, which should eliminate the race condition.
I'm confident this is the root cause. Thank you for your time looking into thisOk, I read trough your posts later. Thanks for the indepth analysis!
Just one thing in advance: NSInternalInconsistencyException is usually throw when the UI thread is accessed from a background thread. Maybe we just have to dispatch the RemoveAllSegments method to run on the UI thread?Hi Thomas,
Thank you so much for your quick feedback and analysis. I really appreciate your suggestion regarding potential UI access from a background thread. That's a crucial point and certainly the most common cause for NSInternalInconsistencyException.
I wanted to give you an update that might be helpful. I continued to run some tests and discovered a detail that, in my specific use case, seems to have completely resolved the instability.
In addition to the workaround I mentioned (using a Grid as a container on iOS), I made one further small change: I moved the initialization of this Grid directly into my page's constructor (instead to OnAppearing), like this:
// In my view/page constructor PageTicketMode = new Grid { VerticalOptions = LayoutOptions.Fill, HorizontalOptions = LayoutOptions.Fill, BackgroundColor = Colors.Transparent };Since implementing this upfront container initialization, I have not been able to reproduce the crash at all, neither on the simulator and on a physical device. I've tried to heavily stress the control with very rapid and continuous selection changes, but the application has remained stable.
This leads me to believe the issue is closely tied to the timing of the .NET Maui layout cycle. It's possible that initializing the parent container early ensures that when the SegmentControl handler executes, the layout system is in a more stable and predictable state.
Perhaps, this prevents the conditions that lead to the crash, whether they're related to threading as you suggested, or to the potential race condition I hypothesized with RemoveAllSegments...
In any case, with this change, the control is now perfectly stable for my needs. I'm sharing this information in case it might be helpful to you or other users who might encounter similar instability in the future. As far as I'm concerned, the issue can be considered happily resolved.
Thanks again for your help and for the great work on this plugin.
Description
On iOS 26, the SegmentedControl becomes completely invisible when placed inside a Shell.TitleView. The issue appears to be caused by layout measurement and rendering changes related to the new "Liquid Glass" UI design of the UINavigationBar.
The control can be forced to appear on the iOS 26 simulator with specific workarounds, but these workarounds fail on a physical device, where the control remains invisible. This suggests a deep issue with the native hardware rendering pipeline on iOS 26.
Steps to Reproduce
Set this container as the TitleView for the page.
Shell.SetTitleView(this, titleContainer);Deploy and run the application on a physical iPhone running iOS 26.
Navigate to the page and observe the navigation bar.
Expected Behavior
TitleViewof the navigation bar, as it was on iOS versions prior to 26.Actual Behavior
HeightRequestand adding children immediately) are applied. The failure is specific to physical hardware.Basic Information
Screenshots, Attachments, Links
Image with:
Screenshot 1: Correct behavior on iOS < 26.
Screenshot 2: The invisible control on a physical iPhone with iOS 26.