Created
October 14, 2019 13:48
-
-
Save softlion/a568b5215c071cb3057dff0bc127fbf8 to your computer and use it in GitHub Desktop.
Xamarin Shimmer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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