Как эффект может определить размер отображения UIImageView, к которому он прикреплен? - PullRequest
0 голосов
/ 16 января 2020

В Xamarin.Forms, Effect может быть присоединено к View. В моем случае вид отображает Image. И эффект делает цветное «свечение» вокруг видимых пикселей изображения. XAML:

<Image Source="{Binding LogoImage}" ...>
    <Image.Effects>
        <effects:GlowEffect Radius="5" Color="White" />
    </Image.Effects>
</Image>

Эффект реализован в виде подкласса RoutingEffect:

public class GlowEffect : Xamarin.Forms.RoutingEffect
{
    public GlowEffect() : base("Core.ImageGlowEffect")
    {
    }
...
}

На каждой платформе есть PlatformEffect для реализации эффекта. Для iOS:

using ...

[assembly: ExportEffect(typeof(Core.Effects.ImageGlowEffect), "ImageGlowEffect")]
namespace Core.Effects
{
  public class ImageGlowEffect : PlatformEffect
  {
    protected override void OnAttached()
    {
        ApplyGlow();
    }

    protected override void OnElementPropertyChanged( PropertyChangedEventArgs e )
    {
        base.OnElementPropertyChanged( e );

        if (e.PropertyName == "Source") {
            ApplyGlow();
        }
    }

    private void ApplyGlow()
    {
        var imageView = Control as UIImageView;
        if (imageView.Image == null)
            return;

        var effect = (GlowEffect)Element.Effects.FirstOrDefault(e => e is GlowEffect);

        if (effect != null) {
            CGRect outSize = AVFoundation.AVUtilities.WithAspectRatio( new CGRect( new CGPoint(), imageView.Image.Size ), imageView.Frame.Size );
                ...
        }
    }
  ...
  }
}

Приведенный выше код работает, если Source изменяется динамически: во время изменения Source размер UIImageView (Bounds или Frame) имеет размер , НО, если Источник установлен статически в XAML, этот лог c не запускается, поэтому единственный вызов ApplyGlow происходит во время OnAttach. К сожалению, во время OnAttach, UIImageView имеет размер (0, 0).

Как заставить этот эффект работать на iOS, со значением c Source?

ПРИМЕЧАНИЕ. Эквивалентный Android эффект работает через обработчик, прикрепленный к ImageView.ViewTreeObserver.PreDraw - когда размер известен. Так что, если существует iOS эквивалентное событие, это будет один из способов решения.


Подробнее:

  • В исходной реализации использовалось исходное изображение size (imageView.Image.Size) - который доступен во время OnAttach. Это может быть сделано для «работы», но не является удовлетворительным: свечение применяется к полноразмерному изображению. Если изображение значительно больше области обзора, свечение становится слишком маленьким в радиусе (iOS сжимает изображение + свечение при визуализации): оно не имеет желаемого вида.

  • ApplyGlow имеет возможность применить оттенок цвета к изображению. Этот оттенок отличается от цвета свечения. Я упоминаю об этом, потому что он ограничивает возможные решения: AFAIK, не может просто установить параметры для изображения и позволить iOS выяснить, как его визуализировать - необходимо явно изменить размер изображения и нарисовать измененное тонированное изображение поверх размытого изображения версия (свечение). Этот код все работает - , если imageView.Bounds.Size (или imageView.Frame.Size) доступен (и ненулевой).

  • С точкой останова в OnElementPropertyChanged, Я проверил, известен ли размер imageView для любого свойства, которое всегда установлено. Нет; если динамически не установлены никакие свойства, все изменения свойств происходят до того, как imageView имеет размер.

1 Ответ

1 голос
/ 16 января 2020

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

Добавьте небольшую задержку перед вызовом ApplyGlow(); в OnAttached. После задержки вы получите imageView.Frame.Size или imageView.Bounds.Size.

protected override async void OnAttached()
{
    await Task.Delay(TimeSpan.FromSeconds(0.3));// it can be 0.2s,0.1s, depending on you
    ApplyGlow();
}

И если вы установили WidthRequest, HeightRequest, вы можете попасть туда без задержки:

private void ApplyGlow()
{
    var imageView = Control as UIImageView;
    if (imageView.Image == null)
        return;

    Console.WriteLine(imageView.Image.Size.Width);
    Console.WriteLine(imageView.Image.Size.Height);

    CoreGraphics.CGSize rect = imageView.Bounds.Size;
    CoreGraphics.CGSize rect2 = imageView.Frame.Size;

    Console.WriteLine(rect);
    Console.WriteLine(rect2);

    double width = (double)Element.GetValue(VisualElement.WidthRequestProperty);
    double height = (double)Element.GetValue(VisualElement.HeightRequestProperty);

    Console.WriteLine(width);
    Console.WriteLine(height);

    double width2 = (double)Element.GetValue(VisualElement.WidthProperty);
    double height2 = (double)Element.GetValue(VisualElement.HeightProperty);

    Console.WriteLine(width2);
    Console.WriteLine(height2);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...