Created
November 16, 2015 11:52
-
-
Save rafaelrmou/7f0c44c05630a88ff009 to your computer and use it in GitHub Desktop.
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
using System; | |
using Xamarin.Forms; | |
using Xamarin.Forms.Platform.iOS; | |
using Badge.iOS; | |
using UIKit; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Drawing; | |
using Foundation; | |
using CoreGraphics; | |
[assembly: ExportRenderer (typeof(Page), typeof(CustomPageRenderer))] | |
[assembly: ExportRenderer (typeof(NavigationPage), typeof(CustomNavigationRenderer))] | |
namespace Badge.iOS | |
{ | |
public class CustomPageRenderer : PageRenderer | |
{ | |
List<CustomToolBarItem> list = new List<CustomToolBarItem> (); | |
public CustomPageRenderer () | |
{ | |
} | |
protected override void OnElementChanged (VisualElementChangedEventArgs e) | |
{ | |
base.OnElementChanged (e); | |
var page = (e.NewElement as Page); | |
if (page != null) { | |
list.AddRange (page.ToolbarItems.Select (n => n as CustomToolBarItem)); | |
} | |
} | |
public override void ViewWillAppear (bool animated) | |
{ | |
base.ViewWillAppear (animated); | |
try { | |
UIBarButtonItem backButton = new UIBarButtonItem (); | |
backButton.Title = ""; | |
NavigationController.NavigationBar.TopItem.BackBarButtonItem = backButton; | |
} catch (Exception) { | |
} | |
try { | |
BadgeView number = new BadgeView (new RectangleF (60, 00, 30, 20)); | |
number.Value = 10; | |
if (this.NavigationItem.RightBarButtonItems.Count () > 0) | |
this.NavigationItem.RightBarButtonItems [1].CustomView.AddSubview (number); | |
} catch (Exception ex) { | |
} | |
} | |
} | |
public class CustomNavigationRenderer : NavigationRenderer | |
{ | |
public override void PushViewController (UIViewController viewController, bool animated) | |
{ | |
base.PushViewController (viewController, animated); | |
try { | |
List<UIBarButtonItem> newItems = new List<UIBarButtonItem> (); | |
foreach (UIBarButtonItem i in TopViewController.NavigationItem.RightBarButtonItems) { | |
BadgeView number = new BadgeView (new RectangleF (18, 1, 40, 40)); | |
number.Font.WithSize (4f); | |
number.Value = 5; | |
// | |
// UIButton newB = new UIButton(UIButtonType.System); | |
// newB.AddSubview(number); | |
// | |
// newB.SetImage(i.Image,UIControlState.Normal); | |
// var newBT = new UIBarButtonItem(newB); | |
// i.CustomView = newB; | |
// | |
// newBT.Action = i.Action; | |
// newBT.Target = i.Target; | |
// newBT.Image = i.Image; | |
UIButton btn = new UIButton (UIButtonType.Custom); | |
btn.Frame = new CGRect (0, 0, 30, 30); | |
btn.BackgroundColor = new UIColor (0, 0, 0.1f, 0); | |
btn.SetImage (i.Image, UIControlState.Normal); | |
btn.Font.WithSize (6); | |
//[btn setFont:[UIFont systemFontOfSize:13]]; | |
btn.AddSubview (number); //Add NKNumberBadgeView as a subview on UIButton | |
i.CustomView = btn; | |
newItems.Add (i); | |
} | |
TopViewController.NavigationItem.RightBarButtonItems = newItems.ToArray (); | |
} catch (Exception ex) { | |
} | |
} | |
} | |
public class BadgeView : UIView | |
{ | |
private int val; | |
private bool hideWhenZero; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="BadgeView"/> class. | |
/// </summary> | |
/// <param name="frame">The frame.</param> | |
public BadgeView (RectangleF frame) | |
: base (frame) | |
{ | |
this.InitState (); | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="BadgeView"/> class. | |
/// </summary> | |
/// <param name="coder">The coder.</param> | |
public BadgeView (NSCoder coder) | |
: base (coder) | |
{ | |
this.InitState (); | |
} | |
/// <summary> | |
/// Gets or sets the Text format for the badge, defaults to just the number | |
/// </summary> | |
/// <value>The text format.</value> | |
public string TextFormat { get; set; } | |
/// <summary> | |
/// Gets or sets the adjustment offset for the text in the badge | |
/// </summary> | |
/// <value>The adjust offset.</value> | |
public PointF AdjustOffset { get; set; } | |
/// <summary> | |
/// Gets or sets the current value displayed in the badge. Updating the value will update the view's display. | |
/// </summary> | |
/// <value>The value.</value> | |
public int Value { | |
get { | |
return this.val; | |
} | |
set { | |
if (this.val != value) { | |
this.val = value; | |
this.Hidden = this.HideWhenZero && this.val == 0; | |
var size = this.BadgeSize; | |
this.Frame = new CGRect (this.Frame.X, this.Frame.Y, size.Width, size.Height); | |
this.SetNeedsDisplay (); | |
} | |
} | |
} | |
/// <summary> | |
/// Gets or sets a value indicating whether the badge view draws a dhadow or not. | |
/// </summary> | |
/// <value><c>true</c> if shadow; otherwise, <c>false</c>.</value> | |
public bool Shadow { get; set; } | |
/// <summary> | |
/// Gets or sets the offset for the shadow, if there is one. | |
/// </summary> | |
/// <value>The shadow offset.</value> | |
public SizeF ShadowOffset { get; set; } | |
/// <summary> | |
/// Gets or sets the base color for the shadow, if there is one. | |
/// </summary> | |
/// <value>The color of the shadow.</value> | |
public UIColor ShadowColor { get; set; } | |
/// <summary> | |
/// Gets or sets a value indicating whether the badge view should be drawn with a shine. | |
/// </summary> | |
/// <value><c>true</c> if shine; otherwise, <c>false</c>.</value> | |
public bool Shine { get; set; } | |
/// <summary> | |
/// Gets or sets the font to be used for drawing the numbers. NOTE: not all fonts are created equal for this purpose. | |
/// Only "system fonts" should be used. | |
/// </summary> | |
/// <value>The font.</value> | |
public UIFont Font { get; set; } | |
/// <summary> | |
/// Gets or sets the color used for the background of the badge. | |
/// </summary> | |
/// <value>The color of the fill.</value> | |
public UIColor FillColor { get; set; } | |
/// <summary> | |
/// Gets or sets the color to be used for drawing the stroke around the badge. | |
/// </summary> | |
/// <value>The color of the stroke.</value> | |
public UIColor StrokeColor { get; set; } | |
/// <summary> | |
/// Gets or sets the width for the stroke around the badge. | |
/// </summary> | |
/// <value>The width of the stroke.</value> | |
public float StrokeWidth { get; set; } | |
/// <summary> | |
/// Gets or sets The color to be used for drawing the badge's numbers. | |
/// </summary> | |
/// <value>The color of the text.</value> | |
public UIColor TextColor { get; set; } | |
/// <summary> | |
/// Gets or sets how the badge image hould be aligned horizontally in the view. | |
/// </summary> | |
/// <value>The alignment.</value> | |
public UITextAlignment Alignment { get; set; } | |
/// <summary> | |
/// Gets the visual size of the badge for the current value. Not the same hing as the size of the view's bounds. | |
/// The badge view bounds should be wider than space needed to draw the badge. | |
/// </summary> | |
/// <value>The size of the badge.</value> | |
public CGSize BadgeSize { | |
get { | |
var numberString = this.Value.ToString (this.TextFormat); | |
CGSize numberSize = new NSString (numberString).StringSize (this.Font); | |
using (CGPath badgePath = this.NewBadgePathForTextSize (numberSize)) { | |
CGRect badgeRect = badgePath.PathBoundingBox; | |
badgeRect.X = 0; | |
badgeRect.Y = 0; | |
badgeRect.Size = new SizeF ((float)Math.Ceiling (badgeRect.Size.Width), (float)Math.Ceiling (badgeRect.Size.Height)); | |
return badgeRect.Size; | |
} | |
} | |
} | |
/// <summary> | |
/// Gets or sets the number of pixels between the number inside the badge and the stroke around the badge. This value | |
/// is approximate, as the font geometry might effectively slightly increase or decrease the apparent pad. | |
/// </summary> | |
/// <value>The pad.</value> | |
public int Pad { get; set; } | |
/// <summary> | |
/// Gets or sets a value indicating whether this badge hides when the value reaches zero. | |
/// </summary> | |
/// <value><c>true</c> if hide when zero; otherwise, <c>false</c>.</value> | |
public bool HideWhenZero { | |
get { | |
return this.hideWhenZero; | |
} | |
set { | |
if (this.hideWhenZero != value) { | |
this.hideWhenZero = value; | |
this.Hidden = this.hideWhenZero && this.val == 0; | |
this.SetNeedsDisplay (); | |
} | |
} | |
} | |
public override CGSize SizeThatFits (CGSize size) | |
{ | |
return base.SizeThatFits (size); | |
} | |
/// <summary> | |
/// Draw the view into the specified rect. | |
/// </summary> | |
/// <param name="rect">The Rect.</param> | |
public override void Draw (CGRect rect) | |
{ | |
CGRect viewBounds = this.Bounds; | |
CGContext curContext = UIGraphics.GetCurrentContext (); | |
var numberString = this.Value.ToString (this.TextFormat); | |
CGSize numberSize = new NSString (numberString).StringSize (this.Font); | |
using (CGPath badgePath = this.NewBadgePathForTextSize (numberSize)) { | |
CGRect badgeRect = badgePath.PathBoundingBox; | |
badgeRect.X = 0; | |
badgeRect.Y = 0; | |
badgeRect.Size = new SizeF ((float)Math.Ceiling (badgeRect.Size.Width), (float)Math.Ceiling (badgeRect.Size.Height)); | |
curContext.SaveState (); | |
curContext.SetLineWidth (this.StrokeWidth); | |
curContext.SetStrokeColor (this.StrokeColor.CGColor); | |
curContext.SetFillColor (this.FillColor.CGColor); | |
// Line stroke straddles the path, so we need to account for the outer portion | |
badgeRect.Size = new CGSize (badgeRect.Size.Width + (float)Math.Ceiling (this.StrokeWidth / 2), badgeRect.Size.Height + (float)Math.Ceiling (this.StrokeWidth / 2)); | |
PointF ctm = new PointF (0f, 0f); | |
switch (this.Alignment) { | |
case UITextAlignment.Justified: | |
case UITextAlignment.Natural: | |
case UITextAlignment.Center: | |
ctm = new PointF ((float)Math.Round ((viewBounds.Size.Width - badgeRect.Size.Width) / 2), (float)Math.Round ((viewBounds.Size.Height - badgeRect.Size.Height) / 2)); | |
break; | |
case UITextAlignment.Left: | |
ctm = new PointF (0.0f, (float)Math.Round ((viewBounds.Size.Height - badgeRect.Size.Height) / 2)); | |
break; | |
case UITextAlignment.Right: | |
ctm = new PointF ((float)(viewBounds.Size.Width - badgeRect.Size.Width), (float)Math.Round (viewBounds.Size.Height - badgeRect.Size.Height) / 2); | |
break; | |
} | |
curContext.TranslateCTM (ctm.X, ctm.Y); | |
if (this.Shadow) { | |
curContext.SaveState (); | |
SizeF blurSize = this.ShadowOffset; | |
curContext.SetShadow (blurSize, 4, this.ShadowColor.CGColor); | |
curContext.BeginPath (); | |
curContext.AddPath (badgePath); | |
curContext.ClosePath (); | |
curContext.DrawPath (CGPathDrawingMode.FillStroke); | |
curContext.RestoreState (); | |
} | |
curContext.BeginPath (); | |
curContext.AddPath (badgePath); | |
curContext.ClosePath (); | |
curContext.DrawPath (CGPathDrawingMode.FillStroke); | |
if (this.Shine) { | |
curContext.BeginPath (); | |
curContext.AddPath (badgePath); | |
curContext.ClosePath (); | |
curContext.Clip (); | |
using (CGColorSpace colorSpace = CGColorSpace.CreateDeviceRGB ()) { | |
nfloat[] shinyColorGradient = new nfloat[8] { 1f, 1f, 1f, 0.8f, 1f, 1f, 1f, 0f }; | |
nfloat[] shinyLocationGradient = new nfloat[2] { 0f, 1f }; | |
using (CGGradient gradient = new CGGradient (colorSpace, shinyColorGradient, shinyLocationGradient)) { | |
curContext.SaveState (); | |
curContext.BeginPath (); | |
curContext.MoveTo (0.0f, 0.0f); | |
nfloat shineStartY = badgeRect.Size.Height * 0.25f; | |
nfloat shineStopY = shineStartY + (badgeRect.Size.Height * 0.4f); | |
curContext.AddLineToPoint (0, shineStartY); | |
curContext.AddCurveToPoint ( | |
0, | |
shineStopY, | |
badgeRect.Size.Width, | |
shineStopY, | |
badgeRect.Size.Width, | |
shineStartY); | |
curContext.AddLineToPoint (badgeRect.Size.Width, 0); | |
curContext.ClosePath (); | |
curContext.Clip (); | |
curContext.DrawLinearGradient ( | |
gradient, | |
new PointF (((float)(badgeRect.Size.Width / 2.0f)), 0.0f), | |
new PointF (((float)(badgeRect.Size.Width / 2.0f)), (float)shineStopY), | |
CGGradientDrawingOptions.DrawsBeforeStartLocation); | |
curContext.RestoreState (); | |
} | |
} | |
} | |
curContext.RestoreState (); | |
curContext.SaveState (); | |
curContext.SetFillColor (this.TextColor.CGColor); | |
PointF textPt = new PointF ( | |
((float)(ctm.X + ((badgeRect.Size.Width - numberSize.Width) / 2) + this.AdjustOffset.X)), | |
((float)(ctm.Y + ((badgeRect.Size.Height - numberSize.Height) / 2) + this.AdjustOffset.Y)) | |
); | |
new NSString (numberString).DrawString (textPt, this.Font); | |
curContext.RestoreState (); | |
} | |
} | |
private void InitState () | |
{ | |
var iosVersion = new Version (UIDevice.CurrentDevice.SystemVersion); | |
this.Opaque = false; | |
this.Pad = 2; | |
this.Font = UIFont.SystemFontOfSize (12); | |
this.Shadow = iosVersion.Major < 7; | |
this.ShadowOffset = new SizeF (0, 3); | |
this.ShadowColor = UIColor.Black.ColorWithAlpha (0.5f); | |
this.Shine = iosVersion.Major < 7; | |
this.Alignment = UITextAlignment.Right; | |
this.FillColor = UIColor.Red; | |
this.StrokeColor = iosVersion.Major < 7 ? UIColor.White : UIColor.Clear; | |
this.StrokeWidth = iosVersion.Major < 7 ? 2.0f : 0.0f; | |
this.TextColor = UIColor.White; | |
this.HideWhenZero = true; | |
this.AdjustOffset = new PointF (0, 0); | |
this.TextFormat = "d"; | |
this.BackgroundColor = UIColor.Clear; | |
} | |
private CGPath NewBadgePathForTextSize (CGSize size) | |
{ | |
float arcRadius = (float)Math.Ceiling ((size.Height + this.Pad) / 2.0f); | |
float badgeWidthAdjustment = (float)(size.Width - (size.Height / 2.0f)); | |
float badgeWidth = 2.0f * arcRadius; | |
if (badgeWidthAdjustment > 0.0) { | |
badgeWidth += badgeWidthAdjustment; | |
} | |
CGPath badgePath = new CGPath (); | |
var m_pi_2 = (float)(Math.PI / 2); | |
badgePath.MoveToPoint (arcRadius, 0.0f); | |
badgePath.AddArc (arcRadius, arcRadius, arcRadius, 3.0f * m_pi_2, m_pi_2, true); | |
badgePath.AddLineToPoint (badgeWidth - arcRadius, 2.0f * arcRadius); | |
badgePath.AddArc (badgeWidth - arcRadius, arcRadius, arcRadius, m_pi_2, 3.0f * m_pi_2, true); | |
badgePath.AddLineToPoint (arcRadius, 0.0f); | |
return badgePath; | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment