Как загрузить svg-изображение в заголовок вкладки в Xamarin

#svg #xamarin.forms #tabs

#svg #xamarin.forms #вкладки

Вопрос:

Я создаю простое приложение, в котором есть 3 вкладки, на каждой вкладке есть значок и текст. Пример проекта, на котором я основываю свой код, существует здесь:

Ссылка

Единственная проблема, которую я обнаружил с ним, это использование пикселизированных изображений для заголовков вкладок, которые я хотел бы изменить на файлы ресурсов SVG.

Я добавил библиотеку загрузки FFImageLoading и добавил поддержку svg отсюда

Ссылка

Мне удалось добавить изображение на одну из страниц содержимого вкладки, но я не вижу svg-изображения, которое я пытаюсь увидеть в заголовке.

Пока это мой код:

TodayPage.xaml.cs

 public partial class TodayPage : ContentPage
    {
        public TodayPage()
        {
            InitializeComponent();
            InitializeComponent();
            // IconImageSource = "today.png"; <-- this will show the pixilated image
            IconImageSource = SvgImageSource.FromResource("today.svg"); //<--not working
            Title = "Today";
        }
    }
  

TodayPage.xaml

 <?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:forms="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"
             x:Class="TabsApp.TodayPage">
    <ContentPage.Content>
        <StackLayout>
            <forms:SvgCachedImage WidthRequest="200" HeightRequest="200" Source="resource://TabsApp.Resources.today.svg"/>
            <Label Text="Today's appointments go here going" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>
  

MainPage.xaml

 <?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" 
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
            xmlns:local="clr-namespace:TabsApp;assembly=TabsApp" 
            x:Class="TabsApp.MainPage">
    <local:TodayPage />
    <NavigationPage Title="Schedule" IconImageSource="schedule.png">
        <x:Arguments>
            <local:SchedulePage />
        </x:Arguments>
    </NavigationPage>
    <local:SettingPage />
</TabbedPage>
  

Спасибо

Редактировать

Благодаря Leon мне удалось загрузить svg-файл в Android. Я все еще пытаюсь загрузить файл svg в iOS.

Мне удалось создать пользовательский инструмент визуализации вкладок в iOS, вот код:

 [assembly: ExportRenderer(typeof(TabPage), typeof(PageTabRenderer))]
    namespace TabsApp.iOS
{
    [Foundation.Preserve(AllMembers = true)]
    public class PageTabRenderer : TabbedRenderer
    {
        protected override async Task<Tuple<UIImage, UIImage>> GetIcon(Page page)
        {
            var navigationPage = page as NavigationPage;
            if (navigationPage != null amp;amp; navigationPage.CurrentPage != null)
            {
                var imageSource = navigationPage.IconImageSource == null ? navigationPage.CurrentPage.IconImageSource : navigationPage.IconImageSource;
                return await this.GetNativeUIImage(imageSource);
            }

            return await this.GetNativeUIImage(page.IconImageSource);
        }

        private async Task<Tuple<UIImage, UIImage>> GetNativeUIImage(ImageSource imageSource)
        {
            var imageicon = await GetNativeImageAsync(imageSource);
            return new Tuple<UIImage, UIImage>(imageicon, null);
        }

        private async Task<UIImage> GetNativeImageAsync(ImageSource imageSource)
        {
            if (imageSource is FileImageSource fileImage amp;amp; fileImage.File.Contains(".svg"))
            {
                var imageicon = await ImageService.Instance.LoadFile(fileImage.File).WithCustomDataResolver(new SvgDataResolver(15, 15, true)).AsUIImageAsync();
                return imageicon.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
            }
            
            else
            {
                var imageicon = await GetUIImage(imageSource);
                return imageicon.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
            }
        }
        
        private Task<UIImage> GetUIImage(ImageSource imageSource)
        {
            var handler = GetImageSourceHandler(imageSource);
            return handler.LoadImageAsync(imageSource);
        }

        private static IImageSourceHandler GetImageSourceHandler(ImageSource source)
        {
            IImageSourceHandler sourceHandler = null;
            if (source is UriImageSource)
                sourceHandler = new ImageLoaderSourceHandler();
            else if (source is FileImageSource)
                sourceHandler = new FileImageSourceHandler();
            else if (source is StreamImageSource)
                sourceHandler = new StreamImagesourceHandler();
            else if (source is FontImageSource)
                sourceHandler = new FontImageSourceHandler();

            return sourceHandler;
        }
      
        
        
    }
    
}
  

this is my mainpage.xaml

 <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" 
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
            xmlns:local="clr-namespace:TabsApp;assembly=TabsApp" 
            x:Class="TabsApp.MainPage">
    <local:TodayPage />
    <NavigationPage Title="Schedule" IconImageSource="today1.svg">
        <x:Arguments>
            <local:SchedulePage />
        </x:Arguments>
    </NavigationPage>
    <local:SettingPage />
</TabbedPage> 
  

I also copied TabPage class but I don’t see the svg icons loading. I tried to debug it and it seems the GetIcon is never called.

****** Edit 2 *******

After more playing with the code here and there I managed to figure it out. If someone will be interested in implementing similar design

main.xaml

     <?xml version="1.0" encoding="UTF-8"?>

<custom:TabPage xmlns="http://xamarin.com/schemas/2014/forms"
                xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                xmlns:local="clr-namespace:TabsApp;assembly=TabsApp"
                xmlns:custom="clr-namespace:TabsApp.custom;assembly=TabsApp"
                x:Class="TabsApp.MainPage"
                xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
                android:TabbedPage.ToolbarPlacement="Bottom"
                android:TabbedPage.IsSmoothScrollEnabled="True"
                android:TabbedPage.IsSwipePagingEnabled="False"
                xmlns:iOS="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
                xmlns:ffimageloadingsvg="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"
                iOS:Page.UseSafeArea="true" NavigationPage.HasNavigationBar="False"
                BarTextColor="{DynamicResource SecondaryTextColor}" UnselectedTabColor="Black" SelectedTabColor="Blue"
                NavigationPage.HasBackButton="False">
   
   
            <local:TodayPage IconImageSource="{x:OnPlatform Android=ic_today, iOS=today-24px.svg}"/>
   
    
   
            <local:SchedulePage IconImageSource="{x:OnPlatform Android=ic_schedule, iOS=schedule-24px.svg}"/>
      
  
            <local:SettingPage IconImageSource="{x:OnPlatform Android=ic_settings, iOS=settings-24px.svg}"/>
     
    
</custom:TabPage>
  

Важно отметить две вещи: IconImageSource будет изменен в зависимости от платформы, SelectedTabColor будет цветом оттенка как для Android, так и для iOS, когда выбран элемент вкладки.

вам нужно преобразовать файлы svg в векторные файлы, которые можно рисовать, как писал Lean, и поместить их в папку / drawable .

Что касается iOS, вам нужно будет добавить пользовательский инструмент визуализации вкладок, который я использовал:

 [assembly: ExportRenderer(typeof(TabPage), typeof(PageTabRenderer))]
namespace TabsApp.iOS
{
    [Foundation.Preserve(AllMembers = true)]
    public class PageTabRenderer : TabbedRenderer
    {
        readonly nfloat imageYOffset = 7.0f;

        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);

            if (TabBar.Items != null)
            {
                foreach (var item in TabBar.Items)
                {
                    item.Title = null;
                    item.ImageInsets = new UIEdgeInsets(imageYOffset, 0, -imageYOffset, 0);
                }
            }
        }
        
        protected override async Task<Tuple<UIImage, UIImage>> GetIcon(Page page)
        {
            var navigationPage = page as NavigationPage;
            if (navigationPage != null amp;amp; navigationPage.CurrentPage != null)
            {
                var imageSource = navigationPage.IconImageSource == null ? navigationPage.CurrentPage.IconImageSource : navigationPage.IconImageSource;
                return await this.GetNativeUIImage(imageSource);
            }

            return await this.GetNativeUIImage(page.IconImageSource);
        }

        private async Task<Tuple<UIImage, UIImage>> GetNativeUIImage(ImageSource imageSource)
        {
            var imageicon = await GetNativeImageAsync(imageSource);
            return new Tuple<UIImage, UIImage>(imageicon, null);
        }

        private async Task<UIImage> GetNativeImageAsync(ImageSource imageSource)
        {
            if (imageSource is FileImageSource fileImage amp;amp; fileImage.File.Contains(".svg"))
            {
                var imageicon = await ImageService.Instance.LoadFile(fileImage.File).WithCustomDataResolver(new SvgDataResolver(15, 15, true)).AsUIImageAsync();
                return imageicon.ImageWithRenderingMode(UIImageRenderingMode.Automatic);
            }
            
            else
            {
                var imageicon = await GetUIImage(imageSource);
                return imageicon.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
            }
        }
        
        private Task<UIImage> GetUIImage(ImageSource imageSource)
        {
            var handler = GetImageSourceHandler(imageSource);
            return handler.LoadImageAsync(imageSource);
        }

        private static IImageSourceHandler GetImageSourceHandler(ImageSource source)
        {
            IImageSourceHandler sourceHandler = null;
            if (source is UriImageSource)
                sourceHandler = new ImageLoaderSourceHandler();
            else if (source is FileImageSource)
                sourceHandler = new FileImageSourceHandler();
            else if (source is StreamImageSource)
                sourceHandler = new StreamImagesourceHandler();
            else if (source is FontImageSource)
                sourceHandler = new FontImageSourceHandler();

            return sourceHandler;
        }
      
        
        
    }
    
}
  

Поместите все файлы * .svg в папку /Resources в папке * .iOS и все.

Комментарии:

1. today.svg добавлен как встроенный ресурс правильно? (извините за очевидный вопрос, но просто проверяю)

Ответ №1:

Я получил тот же результат, вот мой обходной путь для svg-изображения на панели вкладок.

Для Android, пожалуйста, посетите эту страницу https://shapeshifter.design /

Затем импортируйте свой SVG-файл на веб-сайт, затем загрузите XML-файл, как показано ниже (нажмите Import , выберите svg , выберите svg-файл, затем выберите Export , векторное изображение, загрузите XML-файл). Скопируйте XML-файл в папку Android Resource/ Drawable (пожалуйста, проверьте, что действие сборки AndroidResource относится к XML-файлу). Примечание: если вы хотите изменить цвет строки svg-файла, пожалуйста, откройте xml, выберите цвет заливки, измените его.

введите описание изображения здесь

введите описание изображения здесь

Затем вы можете использовать его в pcl XML, как следующий код.

 <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
            xmlns:local="clr-namespace:App3" xmlns:ffimageloadingsvg="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"
            BarBackgroundColor="Green"
            x:Class="App3.MainPage">


    <local:TodayPage IconImageSource="{x:OnPlatform Android=ic_today,iOS=today.svg}"  >
      
    </local:TodayPage>
    <NavigationPage Title="Schedule" IconImageSource="myIcon.png">
        <x:Arguments>
            <local:Page1 />
        </x:Arguments>
    </NavigationPage>
    
</TabbedPage>
  

Вот скриншот запуска.

введите описание изображения здесь

Для IOS, пожалуйста, обратитесь к Dinesh_Official ответу в этой теме:

https://forums.xamarin.com/discussion/179773/how-to-use-svg-image-for-tabbed-page-tabbar-icon

Комментарии:

1. Решение для Android сработало для меня, я все еще не уверен, как реализовать вариант iOS, а также как различать android и iOS, когда дело доходит до загрузки разных ресурсов / рендеринга

2. Lean Lu, нужно ли мне создавать средство визуализации вкладок для каждой вкладки, которая у меня будет?

3. @DemoDemo В IOS вам следует создать пользовательский рендеринг для вашей страницы с вкладками, затем переопределить GetIcon метод, вот демонстрация code.github.com/dinesh4official/XFPage/blob/master/XFPage/XFPage.iOS /. … Вы также можете загрузить его.

4. Я обновил свой вопрос, похоже, GetIcon никогда не вызывается. Я прикрепил свой новый код в исходном сообщении. Я ценю вашу помощь!

Ответ №2:

Для отладки SVG-файлов вот несколько шагов, которые могут помочь.

  1. Будет ли работать другой подтвержденный рабочий SVG, если вы его заменили? Если нет, инициализирована ли загрузка ffimageloading?

  2. Подтвердите свои встроенные ресурсы. (поместите это в App.xaml.cs)

     public static void PrintEmbeddedResources()
    {
        #if DEBUG
        var assembly = typeof(App).GetTypeInfo().Assembly;
        var logTxt = "Embedded Resources:"   Environment.NewLine;
        foreach (var res in assembly.GetManifestResourceNames())
        {
            Debug.WriteLine("found resource: "   res);
        }
        #endif
    }
  
  1. убедитесь, что ваш svg оптимизирован. Вот инструмент, который рекомендует ffimageloading: https://jakearchibald.github.io/svgomg /

  2. загрузка ffimageloading иногда вызывает проблемы с определенными SVG. Проверьте проблемы на их github, чтобы узнать, сообщал ли кто-нибудь еще о проблеме, похожей на вашу, и, надеюсь, опубликовал обходной путь.

Комментарии:

1. 1. Я не уверен, оптимизированы ли они, я скачал один отсюда material.io/resources/icons/?style=baseline 2. Ресурс является встроенным ресурсом, я вижу его в debug, вводящем цикл с правильным именем. 3. Я использовал CachedImageRenderer. Init(true); var ignore = typeof(SvgCachedImage); как в вики ffimageloading