Skip to content

Instantly share code, notes, and snippets.

@softlion
Created October 14, 2019 13:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save softlion/a568b5215c071cb3057dff0bc127fbf8 to your computer and use it in GitHub Desktop.
Save softlion/a568b5215c071cb3057dff0bc127fbf8 to your computer and use it in GitHub Desktop.
Xamarin Shimmer
<?xml version="1.0" encoding="utf-8"?>
<com.facebook.shimmer.ShimmerFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
app:shimmer_duration="2000">
<!-- Your content here.
Its content depends on your UI.
Use the color #353c49 for grey placeholder items that will shimmer
Don't use any background.
Example:
<include layout="@layout/home_common_shimmer_content" />
Example:
<View
android:layout_width="80dp"
android:layout_height="15dp"
android:background="@color/placeholder" />
-->
</com.facebook.shimmer.ShimmerFrameLayout>
//Xamarin iOS
using System;
using System.Runtime.InteropServices;
using CoreAnimation;
using CoreGraphics;
using Foundation;
using UIKit;
namespace Vapolia.Ios.Lib.Ui
{
/// <summary>
/// Usage
/// UIView targetView = ...;
/// targetView.SetIsShimmering(true);
///
/// public override void ViewWillAppear(bool animated)
/// {
/// base.ViewWillAppear(animated);
///
/// //Restart animation on appear
/// noDataView.SetIsShimmering(false);
/// if (shouldShimmer)
/// noDataView.SetIsShimmering(true);
/// }
/// </summary>
public static class ShimmerExtensions
{
private static readonly NSString DescriptiveName = new NSString("Vapolia." + nameof(ShimmerExtensions));
public const string AnimationName = "Vapolia.ShimmerExtensions.Animation";
#region ios stuff to associate an object
private enum AssociationPolicy
{
//ASSIGN = 0,
RETAIN_NONATOMIC = 1,
//COPY_NONATOMIC = 3,
//RETAIN = 01401,
//COPY = 01403,
}
[DllImport("/usr/lib/libobjc.dylib")]
private static extern void objc_setAssociatedObject(IntPtr obj, IntPtr key, IntPtr value, AssociationPolicy policy);
[DllImport("/usr/lib/libobjc.dylib")]
private static extern IntPtr objc_getAssociatedObject(IntPtr obj, IntPtr key);
#endregion
class ShimmerProperties : NSObject
{
//mask gradient is Dark-Light-Dark. Dark = 50% alpha, Light = transparent. The light part is narrow.
public CGColor LightMask = UIColor.White.ColorWithAlpha(1f).CGColor; //This color won't mask the target view, thus it will appear lighter.
public CGColor DarkMask = UIColor.White.ColorWithAlpha(0.5f).CGColor; //This color will mask most of the target view, thus it will appear darker.
public float WidthPercent = 30;
internal UIView targetView;
private IDisposable ef, eb;
public readonly CAGradientLayer Gradient;
public bool IsShimmering = false;
public ShimmerProperties()
{
Gradient = new CAGradientLayer
{
Colors = new[] { DarkMask, LightMask, DarkMask },
//Angle
StartPoint = new CGPoint(0, .35),
EndPoint = new CGPoint(1, .5)
};
//https://stackoverflow.com/questions/7568567/restoring-animation-where-it-left-off-when-app-resumes-from-background
ef = UIApplication.Notifications.ObserveWillEnterForeground((o, _) =>
{
if(IsShimmering)
Start(this);
});
eb = UIApplication.Notifications.ObserveDidEnterBackground((o, _) =>
{
if (IsShimmering)
Stop(this);
});
}
protected override void Dispose(bool disposing)
{
ef?.Dispose();
eb?.Dispose();
ef = eb = null;
base.Dispose(disposing);
}
}
private static ShimmerProperties GetShimmer(this UIView targetView)
{
var handle = objc_getAssociatedObject(targetView.Handle, DescriptiveName.Handle);
if (handle != IntPtr.Zero)
{
var value = ObjCRuntime.Runtime.GetNSObject(handle);
if (value != null)
return (ShimmerProperties)value;
}
var properties = new ShimmerProperties { targetView=targetView};
objc_setAssociatedObject(targetView.Handle, DescriptiveName.Handle, properties.Handle, AssociationPolicy.RETAIN_NONATOMIC);
return properties;
}
//public static void SetShimmerProperties(this UIView container, CGColor lightColor, CGColor darkColor)
//{
// var properties = container.GetShimmer();
// properties.LightColor = lightColor;
// properties.DarkColor = darkColor;
//}
public static bool IsShimmering(this UIView targetView)
{
var properties = targetView.GetShimmer();
return properties.IsShimmering;
}
public static void SetIsShimmering(this UIView targetView, bool isShimmering)
{
var properties = targetView.GetShimmer();
if(properties.IsShimmering == isShimmering)
return;
if(isShimmering)
Start(properties);
else
{
Stop(properties);
properties.IsShimmering = false;
}
}
private static void Stop(ShimmerProperties properties)
{
properties.Gradient.RemoveAnimation(AnimationName);
var layer = properties.targetView.Layer;
var mask = layer.Mask;
if (mask != null)
{
layer.Mask = null;
layer.SetNeedsDisplay();
}
}
private static void Start(ShimmerProperties properties)
{
properties.IsShimmering = true;
var targetView = properties.targetView;
var gradient = properties.Gradient;
var bounds = targetView.Bounds;
gradient.Frame = new CGRect(-2 * bounds.Width, 0, 4 * bounds.Width, bounds.Height);
var width = properties.WidthPercent / 4f/100f; //Width = width% * (4xBounds.Width)
gradient.Locations = new NSNumber[] { 0.00, width / 2, width }; //mandatory
targetView.Layer.Mask = gradient;
//Test gradient visually
//container.Layer.AddSublayer(gradient);
#region animate gradient's Locations property
var animation = CABasicAnimation.FromKeyPath("locations");
animation.Duration = 2.0;
animation.RepeatCount = float.PositiveInfinity;
animation.From = NSArray.FromObjects(0.00, width / 2, width);
animation.To = NSArray.FromObjects(1 - width, 1 - (width / 2), 1.00);
CATransaction.Begin();
CATransaction.CompletionBlock = () => Stop(properties);
gradient.AddAnimation(animation, AnimationName);
CATransaction.Commit();
#endregion
}
}
}
//Xamarin Android
using Android.Runtime;
using Facebook.Shimmer;
using MvvmCross.Binding.Bindings.Target.Construction;
using MvvmCross.Platforms.Android.Binding.Target;
namespace Vapolia.Lib.MvxUi.TargetBindings
{
public class ShimmeringTargetBinding : MvxAndroidTargetBinding<ShimmerFrameLayout, bool>
{
public const string Name = "Shimmering";
public static void Register(IMvxTargetBindingFactoryRegistry registry)
{
registry.RegisterCustomBindingFactory<ShimmerFrameLayout>(Name, view => new ShimmeringTargetBinding(view));
}
public ShimmeringTargetBinding(ShimmerFrameLayout target) : base(target)
{
}
protected override void SetValueImpl(ShimmerFrameLayout target, bool value)
{
if(value)
target.ShowShimmer(true);
else
target.HideShimmer();
}
}
[Preserve(AllMembers = true)]
public class ShimmerLinkerIncludeCore
{
public void FacebookShimmer()
{
_ = new ShimmerFrameLayout(null, null, 0, 0);
_ = new ShimmerFrameLayout(null, null, 0);
_ = new ShimmerFrameLayout(null, null);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment