Skip to content

Instantly share code, notes, and snippets.

@jonlipsky
Created August 12, 2014 05:38
Show Gist options
  • Save jonlipsky/ae7e06b8adabff292b90 to your computer and use it in GitHub Desktop.
Save jonlipsky/ae7e06b8adabff292b90 to your computer and use it in GitHub Desktop.
CustomAlertView
using System;
using MonoTouch.UIKit;
using MonoTouch.Foundation;
using System.Collections.Generic;
using System.Drawing;
using MonoTouch.CoreGraphics;
using MonoTouch.CoreAnimation;
using System.Threading.Tasks;
/**
* Copyright (c) 2014 Jon Lipsky
*
* Licensed under The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*
* This is a C# port (with a few enhancements) of the Objective-C "ios-custom-alertview"
* found here: https://github.com/wimagguc/ios-custom-alertview
*
* Usage:
*
* var myview = new UIView(...);
* var alert = new CustomAlertView(myview, "Cancel, "OK");
* var button = await alert.ShowAsync();
* if (button == 1)
* {
* ... do something...
* }
*/
namespace Elevenworks.MonoTouch
{
public delegate void CustomAlertButtonPressed(CustomAlertView sender, int index);
public class CustomAlertView : UIView, IDisposable
{
private const float defaultButtonHeight = 50;
private const float defaultButtonSpacerHeight = 1;
private const float alertViewCornerRadius = 7;
private const float motionEffectExtent = 10.0f;
public event CustomAlertButtonPressed ButtonPressed;
private float buttonHeight = 0;
private float buttonSpacerHeight = 0;
private readonly string[] buttonTitles;
private readonly List<NSObject> eventListeners = new List<NSObject>();
public UIView ContentView {get; private set;}
public UIView AlertView {get; private set;}
public UIButton[] Buttons { get; private set; }
public bool UseMotionEffects {get; set;}
public CustomAlertView(UIView contentView = null, string cancelButton = "Dismiss", params string[] otherButtons)
{
this.ContentView = contentView;
UseMotionEffects = true;
buttonTitles = new string[otherButtons.Length + 1];
buttonTitles [0] = cancelButton;
Array.Copy (otherButtons, 0, buttonTitles, 1, otherButtons.Length);
}
public void Show()
{
AddObserver(UIDevice.OrientationDidChangeNotification, DeviceOrientationDidChange);
AddObserver(UIKeyboard.WillShowNotification, KeyboardWillShow);
AddObserver(UIKeyboard.WillHideNotification, KeyboardWillHide);
AlertView = CreateAlertView();
AlertView.Layer.ShouldRasterize = true;
AlertView.Layer.RasterizationScale = UIScreen.MainScreen.Scale;
Layer.ShouldRasterize = true;
Layer.RasterizationScale = UIScreen.MainScreen.Scale;
if (UseMotionEffects)
{
ApplyMotionEffects();
}
AlertView.Layer.Opacity = 0.5f;
AlertView.Layer.Transform = CATransform3D.MakeScale(1.3f, 1.3f, 1.0f);
BackgroundColor = UIColor.FromRGBA(0, 0, 0, 0);
AddSubview(AlertView);
Transform = CalculateTransform ();
var width = Math.Min (Bounds.Width, Bounds.Height);
var height = Math.Max (Bounds.Width, Bounds.Height);
Frame = new RectangleF(0, 0, width, height);
UIApplication.SharedApplication.Windows[0].AddSubview(this);
UIView.Animate(0.2f, 0.0, UIViewAnimationOptions.CurveEaseInOut, () =>
{
BackgroundColor = UIColor.FromRGBA(0, 0, 0, 0.4f);
AlertView.Layer.Opacity = 1.0f;
AlertView.Layer.Transform = CATransform3D.MakeScale(1, 1, 1);
}, null);
}
public Task<int> ShowAsync()
{
var tcs = new TaskCompletionSource<int> ();
this.ButtonPressed += (sender, index) =>
{
tcs.TrySetResult(index);
Close();
};
Show();
return tcs.Task;
}
public void Close()
{
CATransform3D currentTransform = AlertView.Layer.Transform;
float startRotation = ((NSNumber)AlertView.ValueForKeyPath(new NSString("layer.transform.rotation.z"))).FloatValue;
CATransform3D rotation = CATransform3D.MakeRotation(-startRotation + (float)Math.PI * 270.0f / 180.0f, 0.0f, 0.0f, 0.0f);
AlertView.Layer.Transform = rotation.Concat(CATransform3D.MakeScale(1, 1, 1));
AlertView.Layer.Opacity = 1.0f;
UIView.Animate(0.2f, 0.0, UIViewAnimationOptions.TransitionNone, () =>
{
BackgroundColor = UIColor.FromRGBA(0.0f, 0.0f, 0.0f, 0.0f);
AlertView.Layer.Transform = currentTransform.Concat(CATransform3D.MakeScale(0.6f, 0.6f, 1.0f));
AlertView.Layer.Opacity = 0.0f;
}, () =>
{
foreach (UIView v in Subviews)
{
v.RemoveFromSuperview();
}
RemoveFromSuperview();
RemoveObservers();
});
}
private void AddObserver(NSString name, Action<NSNotification> action)
{
eventListeners.Add(NSNotificationCenter.DefaultCenter.AddObserver(name,action));
}
private void RemoveObservers()
{
NSNotificationCenter.DefaultCenter.RemoveObservers(eventListeners);
eventListeners.Clear();
}
private UIView CreateAlertView()
{
if (ContentView == null)
{
ContentView = new UIView(new RectangleF(0, 0, 300, 150));
}
var screenSize = CalculateScreenSize();
Frame = new RectangleF(0, 0, screenSize.Width, screenSize.Height);
var dialogSize = CalculateAlertViewSize();
var alertX = (screenSize.Width - dialogSize.Width) / 2;
var alertY = (screenSize.Height - dialogSize.Height) / 2;
var alertView = new UIView(new RectangleF(alertX,alertY, dialogSize.Width, dialogSize.Height));
var gradientLayer = new CAGradientLayer();
gradientLayer.Frame = alertView.Bounds;
gradientLayer.Colors = new []
{
new CGColor(218.0f / 255.0f, 218.0f / 255.0f, 218.0f / 255.0f, 1.0f),
new CGColor(233.0f / 255.0f, 233.0f / 255.0f, 233.0f / 255.0f, 1.0f),
new CGColor(218.0f / 255.0f, 218.0f / 255.0f, 218.0f / 255.0f, 1.0f)
};
gradientLayer.CornerRadius = alertViewCornerRadius;
alertView.Layer.InsertSublayer(gradientLayer, 0);
alertView.Layer.CornerRadius = alertViewCornerRadius;
var lineView = new UIView(new RectangleF(0, alertView.Bounds.Size.Height - buttonHeight - buttonSpacerHeight, alertView.Bounds.Size.Width, buttonSpacerHeight));
lineView.BackgroundColor = UIColor.FromRGBA(198.0f / 255.0f, 198.0f / 255.0f, 198.0f / 255.0f, 1.0f);
alertView.AddSubview(lineView);
alertView.AddSubview(ContentView);
AddButtonsToAlertView(alertView);
return alertView;
}
private void AddButtonsToAlertView(UIView container)
{
if (buttonTitles == null)
{
return;
}
Buttons = new UIButton[buttonTitles.Length];
var buttonWidth = container.Bounds.Size.Width / buttonTitles.Length;
for (int i = 0; i < buttonTitles.Length; i++)
{
var button = new UIButton(UIButtonType.Custom);
button.Frame = new RectangleF(i * buttonWidth, container.Bounds.Size.Height - buttonHeight, buttonWidth, buttonHeight);
button.TouchUpInside += HandleButtonPressed;
button.Tag = i;
button.SetTitle(buttonTitles[i], UIControlState.Normal);
button.SetTitleColor(UIColor.FromRGBA(0.0f, 0.5f, 1.0f, 1.0f), UIControlState.Normal);
button.SetTitleColor(UIColor.FromRGBA(0.2f, 0.2f, 0.2f, 0.5f), UIControlState.Highlighted);
button.TitleLabel.Font = UIFont.BoldSystemFontOfSize(17.0f);
button.Layer.CornerRadius = alertViewCornerRadius;
container.AddSubview(button);
Buttons [i] = button;
}
}
private void HandleButtonPressed(object sender, EventArgs e)
{
if (ButtonPressed != null)
{
var button = sender as UIButton;
if (button != null)
{
var tag = button.Tag;
ButtonPressed (this, tag);
}
}
}
private void ApplyMotionEffects()
{
var horizontalEffect = new UIInterpolatingMotionEffect ("center.x", UIInterpolatingMotionEffectType.TiltAlongHorizontalAxis)
{
MinimumRelativeValue = new NSNumber (-motionEffectExtent),
MaximumRelativeValue = new NSNumber (motionEffectExtent),
};
var verticalEffect = new UIInterpolatingMotionEffect("center.y", UIInterpolatingMotionEffectType.TiltAlongVerticalAxis)
{
MinimumRelativeValue = new NSNumber(-motionEffectExtent),
MaximumRelativeValue = new NSNumber(motionEffectExtent),
};
var motionEffectGroup = new UIMotionEffectGroup();
{
MotionEffects = new [] { horizontalEffect, verticalEffect };
}
AlertView.AddMotionEffect(motionEffectGroup);
}
public void DeviceOrientationDidChange(NSNotification notification)
{
var transform = CalculateTransform ();
UIView.Animate(0.2f, 0.0, UIViewAnimationOptions.TransitionNone, () =>
{
AlertView.Transform = transform;
},
null);
}
private void KeyboardWillShow(NSNotification notification)
{
var frame = CalculateAlertViewFrame ();
var keyboardSize = UIKeyboard.FrameBeginFromNotification (notification);
var interfaceOrientation = UIApplication.SharedApplication.StatusBarOrientation;
if (interfaceOrientation == UIInterfaceOrientation.LandscapeLeft || interfaceOrientation == UIInterfaceOrientation.LandscapeRight)
{
float tmp = keyboardSize.Height;
keyboardSize.Height = keyboardSize.Width;
keyboardSize.Width = tmp;
}
UIView.Animate(0.2f, 0.0, UIViewAnimationOptions.TransitionNone, () =>
{
frame.Y -= keyboardSize.Height / 2;
AlertView.Frame = frame;
}, null);
}
private void KeyboardWillHide(NSNotification notification)
{
var frame = CalculateAlertViewFrame ();
UIView.Animate(0.2f, 0.0, UIViewAnimationOptions.TransitionNone, () =>
{
AlertView.Frame = frame;
}, null);
}
private SizeF CalculateAlertViewSize()
{
var dialogWidth = ContentView.Frame.Size.Width;
var dialogHeight = ContentView.Frame.Size.Height + buttonHeight + buttonSpacerHeight;
// Ensure that the dialog width and height are even numbers so that when rotated (transformed) it
// still lines up with pixel boundaries.
if (dialogWidth % 2 == 1) dialogWidth += 1;
if (dialogHeight % 2 == 1) dialogHeight += 1;
return new SizeF(dialogWidth, dialogHeight);
}
private SizeF CalculateScreenSize()
{
if (buttonTitles != null && buttonTitles.Length > 0)
{
buttonHeight = defaultButtonHeight;
buttonSpacerHeight = defaultButtonSpacerHeight;
}
else
{
buttonHeight = 0;
buttonSpacerHeight = 0;
}
var screenSize = UIScreen.MainScreen.Bounds.Size;
var interfaceOrientation = UIApplication.SharedApplication.StatusBarOrientation;
if (interfaceOrientation == UIInterfaceOrientation.LandscapeLeft || interfaceOrientation == UIInterfaceOrientation.LandscapeRight)
{
float tmp = screenSize.Width;
screenSize.Width = screenSize.Height;
screenSize.Height = tmp;
}
return screenSize;
}
private RectangleF CalculateAlertViewFrame()
{
var screenSize = CalculateScreenSize();
var dialogSize = CalculateAlertViewSize();
var alertX = (screenSize.Width - dialogSize.Width) / 2;
var alertY = (screenSize.Height - dialogSize.Height) / 2;
return new RectangleF(alertX, alertY, dialogSize.Width, dialogSize.Height);
}
private CGAffineTransform CalculateTransform()
{
var interfaceOrientation = UIApplication.SharedApplication.StatusBarOrientation;
var startRotation = ((NSNumber)ValueForKeyPath(new NSString("layer.transform.rotation.z"))).FloatValue;
CGAffineTransform transform;
switch (interfaceOrientation)
{
case UIInterfaceOrientation.LandscapeLeft :
transform = CGAffineTransform.MakeRotation(-startRotation + (float)Math.PI * 1.5f);
break;
case UIInterfaceOrientation.LandscapeRight :
transform = CGAffineTransform.MakeRotation(-startRotation + (float)Math.PI * 0.5f);
break;
case UIInterfaceOrientation.PortraitUpsideDown :
transform = CGAffineTransform.MakeRotation(-startRotation + (float)Math.PI);
break;
default :
transform = CGAffineTransform.MakeRotation(-startRotation);
break;
}
return transform;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment