Forum Xamarin.Forms

How to handle Dynamic Type (the "Larger Text" accessibility option) on iOS without re-starting app?

JohnHardmanJohnHardman GBUniversity admin

When a Xamarin.Forms app makes use of NamedSize.Body, NamedSize.Caption, NamedSize.Header, NamedSize.Subtitle and/or NamedSize.Title, in order to support the "Larger Text" (Dynamic Type) accessibility option on iOS, the out-of-the-box support requires the app to be re-started in order to pick up changes made in the accessibility settings whilst the app is running.

Has anybody come up with a method of changing font sizes to take into account the settings changes without re-starting the app?

I haven't done this yet, but I assume that it could be done by creating a custom renderer to override PreferredContentSizeDidChangeForChildContentContainer, and publishing a MessagingCenter message from that override. Any portable View layer code subscribed to that message would when received then set the FontSize of each View again (or update the Style that is being used by each View). Whilst I think that would work, it's a truly horrible solution, so I'm hoping somebody has a better alternative.

Best Answer

  • JohnHardmanJohnHardman GBUniversity admin
    edited November 2020 Accepted Answer

    @JohnHardman said:

    @ColeX said:
    You could subscribe the notification of ContentSizeCategoryChangedNotification , the callback would trigger while font size changes in accessibility settings .

    See my answer here : https://forums.xamarin.com/discussion/comment/424432/#Comment_424432 .

    Based on your other post, I've identified the following as how to observe the changed event, including getting the token so that resources can be cleaned up when no longer required.

    Foundation.NSObject observerToken
      = UIApplication.Notifications.ObserveContentSizeCategoryChanged(
          (s,e) =>
          {
          });
    

    That's a useful first step assuming that's correct, but that still leaves the font handling. As far as I can see, the font handling could either be done in portable code (as per the horrible solution I described in my original post) or could be done in custom renderers (presumably also requiring custom controls in order to identify the NamedSize being used). If the change of size were handled in the renderer, the View containing the text may have to increase in size to hold the larger text. That means that a layout cycle is required so that other Views on the ContentPage adjust accordingly. How would/could you do that in a renderer-based solution?

    I've had a thought, that should work for me because of the way my code is structured. Rather than implement custom renderers etc., when that event is fired I can flush my Application's ResourceDictionary, then simply publish a MessagingCenter message (as per my original post), and then have my base ContentPage class react to that message by rebuilding the UI for each page (taking into account the various bits required to avoid resource leaks). That should work and not involve much code due to how my code is structured. For anybody else trying to do the same it might require a fair bit of re-factoring.

    [Edit - just done it. Yes, this works]

Answers

  • ColeXColeX Member, Xamarin Team Xamurai
    edited November 2020

    You could subscribe the notification of ContentSizeCategoryChangedNotification , the callback would trigger while font size changes in accessibility settings .

    See my answer here : https://forums.xamarin.com/discussion/comment/424432/#Comment_424432 .

  • JohnHardmanJohnHardman GBUniversity admin
    edited November 2020

    @ColeX said:
    You could subscribe the notification of ContentSizeCategoryChangedNotification , the callback would trigger while font size changes in accessibility settings .

    See my answer here : https://forums.xamarin.com/discussion/comment/424432/#Comment_424432 .

    Based on your other post, I've identified the following as how to observe the changed event, including getting the token so that resources can be cleaned up when no longer required.

    Foundation.NSObject observerToken
        = UIApplication.Notifications.ObserveContentSizeCategoryChanged(
            (s,e) =>
            {
            });
    

    That's a useful first step assuming that's correct, but that still leaves the font handling. As far as I can see, the font handling could either be done in portable code (as per the horrible solution I described in my original post) or could be done in custom renderers (presumably also requiring custom controls in order to identify the NamedSize being used). If the change of size were handled in the renderer, the View containing the text may have to increase in size to hold the larger text. That means that a layout cycle is required so that other Views on the ContentPage adjust accordingly. How would/could you do that in a renderer-based solution?

  • JohnHardmanJohnHardman GBUniversity admin
    edited November 2020 Accepted Answer

    @JohnHardman said:

    @ColeX said:
    You could subscribe the notification of ContentSizeCategoryChangedNotification , the callback would trigger while font size changes in accessibility settings .

    See my answer here : https://forums.xamarin.com/discussion/comment/424432/#Comment_424432 .

    Based on your other post, I've identified the following as how to observe the changed event, including getting the token so that resources can be cleaned up when no longer required.

    Foundation.NSObject observerToken
      = UIApplication.Notifications.ObserveContentSizeCategoryChanged(
          (s,e) =>
          {
          });
    

    That's a useful first step assuming that's correct, but that still leaves the font handling. As far as I can see, the font handling could either be done in portable code (as per the horrible solution I described in my original post) or could be done in custom renderers (presumably also requiring custom controls in order to identify the NamedSize being used). If the change of size were handled in the renderer, the View containing the text may have to increase in size to hold the larger text. That means that a layout cycle is required so that other Views on the ContentPage adjust accordingly. How would/could you do that in a renderer-based solution?

    I've had a thought, that should work for me because of the way my code is structured. Rather than implement custom renderers etc., when that event is fired I can flush my Application's ResourceDictionary, then simply publish a MessagingCenter message (as per my original post), and then have my base ContentPage class react to that message by rebuilding the UI for each page (taking into account the various bits required to avoid resource leaks). That should work and not involve much code due to how my code is structured. For anybody else trying to do the same it might require a fair bit of re-factoring.

    [Edit - just done it. Yes, this works]

Sign In or Register to comment.