Initial commit
This commit is contained in:
BIN
Binary file not shown.
+59
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// MPNotificationView.h
|
||||
// Moped
|
||||
//
|
||||
// Created by Engin Kurutepe on 1/2/13.
|
||||
// Copyright (c) 2013 Moped Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
extern NSString *kMPNotificationViewTapReceivedNotification;
|
||||
|
||||
typedef void (^MPNotificationSimpleAction)(id);
|
||||
@protocol MPNotificationViewDelegate;
|
||||
|
||||
@interface MPNotificationView : UIView
|
||||
|
||||
@property (nonatomic, strong) UILabel *textLabel;
|
||||
@property (nonatomic, strong) UILabel *detailTextLabel;
|
||||
@property (nonatomic, strong) UIImageView *imageView;
|
||||
@property (nonatomic, assign) id<MPNotificationViewDelegate> delegate;
|
||||
|
||||
@property (nonatomic) NSTimeInterval duration;
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
detail:(NSString*)detail
|
||||
image:(UIImage*)image
|
||||
andDuration:(NSTimeInterval)duration;
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
detail:(NSString*)detail
|
||||
andDuration:(NSTimeInterval)duration;
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
andDetail:(NSString*)detail;
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
detail:(NSString*)detail
|
||||
image:(UIImage*)image
|
||||
duration:(NSTimeInterval)duration
|
||||
andTouchBlock:(MPNotificationSimpleAction)block;
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
detail:(NSString*)detail
|
||||
duration:(NSTimeInterval)duration
|
||||
andTouchBlock:(MPNotificationSimpleAction)block;
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
detail:(NSString*)detail
|
||||
andTouchBlock:(MPNotificationSimpleAction)block;
|
||||
|
||||
@end
|
||||
|
||||
@protocol MPNotificationViewDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
- (void)didTapOnNotificationView:(MPNotificationView *)notificationView;
|
||||
|
||||
@end
|
||||
+495
@@ -0,0 +1,495 @@
|
||||
//
|
||||
// MPNotificationView.m
|
||||
// Moped
|
||||
//
|
||||
// Created by Engin Kurutepe on 1/2/13.
|
||||
// Copyright (c) 2013 Moped Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPNotificationView.h"
|
||||
#import "OBGradientView.h"
|
||||
|
||||
#define kMPNotificationHeight 40.0f
|
||||
#define kMPNotificationIPadWidth 480.0f
|
||||
#define RADIANS(deg) ((deg) * M_PI / 180.0f)
|
||||
|
||||
static CGRect notificationRect()
|
||||
{
|
||||
if (UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation]))
|
||||
{
|
||||
return CGRectMake(0.0f, 0.0f, [UIScreen mainScreen].bounds.size.height, kMPNotificationHeight);
|
||||
}
|
||||
|
||||
return CGRectMake(0.0f, 0.0f, [UIScreen mainScreen].bounds.size.width, kMPNotificationHeight);
|
||||
}
|
||||
|
||||
NSString *kMPNotificationViewTapReceivedNotification = @"kMPNotificationViewTapReceivedNotification";
|
||||
|
||||
#pragma mark MPNotificationWindow
|
||||
|
||||
@interface MPNotificationWindow : UIWindow
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *notificationQueue;
|
||||
@property (nonatomic, strong) UIView *currentNotification;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPNotificationWindow
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
self.windowLevel = UIWindowLevelStatusBar + 1;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
_notificationQueue = [[NSMutableArray alloc] initWithCapacity:4];
|
||||
|
||||
UIView *topHalfBlackView = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetMinX(frame),
|
||||
CGRectGetMinY(frame),
|
||||
CGRectGetWidth(frame),
|
||||
0.5 * CGRectGetHeight(frame))];
|
||||
|
||||
topHalfBlackView.backgroundColor = [UIColor blackColor];
|
||||
topHalfBlackView.layer.zPosition = -100;
|
||||
topHalfBlackView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
|
||||
[self addSubview:topHalfBlackView];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(willRotateScreen:)
|
||||
name:UIApplicationWillChangeStatusBarFrameNotification
|
||||
object:nil];
|
||||
|
||||
[self rotateStatusBarWithFrame:frame];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) willRotateScreen:(NSNotification *)notification
|
||||
{
|
||||
CGRect notificationBarFrame = notificationRect();
|
||||
|
||||
if (self.hidden)
|
||||
{
|
||||
[self rotateStatusBarWithFrame:notificationBarFrame];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self rotateStatusBarAnimatedWithFrame:notificationBarFrame];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) rotateStatusBarAnimatedWithFrame:(CGRect)frame
|
||||
{
|
||||
CGFloat duration = [UIApplication sharedApplication].statusBarOrientationAnimationDuration;
|
||||
[UIView animateWithDuration:duration
|
||||
animations:^{
|
||||
self.alpha = 0;
|
||||
} completion:^(BOOL finished) {
|
||||
[self rotateStatusBarWithFrame:frame];
|
||||
[UIView animateWithDuration:duration
|
||||
animations:^{
|
||||
self.alpha = 1;
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
- (void) rotateStatusBarWithFrame:(CGRect)frame
|
||||
{
|
||||
BOOL isPortrait = (frame.size.width == [UIScreen mainScreen].bounds.size.width);
|
||||
|
||||
if (isPortrait)
|
||||
{
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
|
||||
{
|
||||
frame.size.width = kMPNotificationIPadWidth;
|
||||
}
|
||||
|
||||
if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown)
|
||||
{
|
||||
frame.origin.y = [UIScreen mainScreen].bounds.size.height - kMPNotificationHeight;
|
||||
self.transform = CGAffineTransformMakeRotation(RADIANS(180.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
self.transform = CGAffineTransformIdentity;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
frame.size.height = frame.size.width;
|
||||
frame.size.width = kMPNotificationHeight;
|
||||
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
|
||||
{
|
||||
frame.size.height = kMPNotificationIPadWidth;
|
||||
}
|
||||
|
||||
if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft)
|
||||
{
|
||||
frame.origin.x = [UIScreen mainScreen].bounds.size.width - frame.size.width;
|
||||
self.transform = CGAffineTransformMakeRotation(RADIANS(90.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
self.transform = CGAffineTransformMakeRotation(RADIANS(-90.0f));
|
||||
}
|
||||
}
|
||||
|
||||
self.frame = frame;
|
||||
CGPoint center = self.center;
|
||||
if (isPortrait)
|
||||
{
|
||||
center.x = CGRectGetMidX([UIScreen mainScreen].bounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
center.y = CGRectGetMidY([UIScreen mainScreen].bounds);
|
||||
}
|
||||
self.center = center;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
static MPNotificationWindow * __notificationWindow = nil;
|
||||
static CGFloat const __imagePadding = 8.0f;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark MPNotificationView
|
||||
|
||||
@interface MPNotificationView ()
|
||||
|
||||
|
||||
@property (nonatomic, strong) OBGradientView * contentView;
|
||||
@property (nonatomic, copy) MPNotificationSimpleAction tapBlock;
|
||||
@property (nonatomic, strong) UITapGestureRecognizer *tapGestureRecognizer;
|
||||
|
||||
+ (void) showNextNotification;
|
||||
+ (UIImage*) screenImageWithRect:(CGRect)rect;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPNotificationView
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
_delegate = nil;
|
||||
[self removeGestureRecognizer:_tapGestureRecognizer];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
CGFloat notificationWidth = notificationRect().size.width;
|
||||
|
||||
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
|
||||
_contentView = [[OBGradientView alloc] initWithFrame:self.bounds];
|
||||
_contentView.colors = @[(id)[[UIColor colorWithWhite:0.99f alpha:1.0f] CGColor],
|
||||
(id)[[UIColor colorWithWhite:0.9f alpha:1.0f] CGColor]];
|
||||
|
||||
_contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
_contentView.layer.cornerRadius = 0.0f;
|
||||
_contentView.clipsToBounds = YES;
|
||||
[self addSubview:_contentView];
|
||||
|
||||
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(6, 6, 28, 28)];
|
||||
_imageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
_imageView.layer.cornerRadius = 4.0f;
|
||||
_imageView.clipsToBounds = YES;
|
||||
[self addSubview:_imageView];
|
||||
|
||||
UIFont *textFont = [UIFont boldSystemFontOfSize:14.0f];
|
||||
CGRect textFrame = CGRectMake(__imagePadding + CGRectGetMaxX(_imageView.frame),
|
||||
2,
|
||||
notificationWidth - __imagePadding * 2 - CGRectGetMaxX(_imageView.frame),
|
||||
textFont.lineHeight);
|
||||
_textLabel = [[UILabel alloc] initWithFrame:textFrame];
|
||||
_textLabel.font = textFont;
|
||||
_textLabel.numberOfLines = 1;
|
||||
_textLabel.textAlignment = NSTextAlignmentLeft;
|
||||
_textLabel.lineBreakMode = UILineBreakModeTailTruncation;
|
||||
_textLabel.backgroundColor = [UIColor clearColor];
|
||||
[_contentView addSubview:_textLabel];
|
||||
|
||||
UIFont *detailFont = [UIFont systemFontOfSize:13.0f];
|
||||
CGRect detailFrame = CGRectMake(CGRectGetMinX(textFrame),
|
||||
CGRectGetMaxY(textFrame),
|
||||
notificationWidth - __imagePadding * 2 - CGRectGetMaxX(_imageView.frame),
|
||||
detailFont.lineHeight);
|
||||
|
||||
_detailTextLabel = [[UILabel alloc] initWithFrame:detailFrame];
|
||||
_detailTextLabel.font = detailFont;
|
||||
_detailTextLabel.numberOfLines = 1;
|
||||
_detailTextLabel.textAlignment = NSTextAlignmentLeft;
|
||||
_detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation;
|
||||
_detailTextLabel.backgroundColor = [UIColor clearColor];
|
||||
[_contentView addSubview:_detailTextLabel];
|
||||
|
||||
UIView *bottomLine = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetMinX(frame),
|
||||
CGRectGetHeight(frame) - 1.0f,
|
||||
CGRectGetWidth(frame),
|
||||
1.0f)];
|
||||
bottomLine.backgroundColor = [UIColor colorWithWhite:0.92 alpha:1.0];
|
||||
|
||||
[_contentView addSubview:bottomLine];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
andDetail:(NSString*)detail
|
||||
{
|
||||
return [self notifyWithText:text
|
||||
detail:detail
|
||||
andDuration:2.0f];
|
||||
}
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
detail:(NSString*)detail
|
||||
andDuration:(NSTimeInterval)duration
|
||||
{
|
||||
return [self notifyWithText:text
|
||||
detail:detail
|
||||
image:nil
|
||||
andDuration:duration];
|
||||
}
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
detail:(NSString*)detail
|
||||
image:(UIImage*)image
|
||||
andDuration:(NSTimeInterval)duration
|
||||
{
|
||||
return [self notifyWithText:text
|
||||
detail:detail
|
||||
image:image
|
||||
duration:duration
|
||||
andTouchBlock:nil];
|
||||
}
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
detail:(NSString*)detail
|
||||
duration:(NSTimeInterval)duration
|
||||
andTouchBlock:(MPNotificationSimpleAction)block
|
||||
{
|
||||
return [self notifyWithText:text
|
||||
detail:detail
|
||||
image:nil
|
||||
duration:duration
|
||||
andTouchBlock:block];
|
||||
}
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
detail:(NSString*)detail
|
||||
andTouchBlock:(MPNotificationSimpleAction)block
|
||||
{
|
||||
return [self notifyWithText:text
|
||||
detail:detail
|
||||
image:nil
|
||||
duration:2.0
|
||||
andTouchBlock:block];
|
||||
}
|
||||
|
||||
+ (MPNotificationView *) notifyWithText:(NSString*)text
|
||||
detail:(NSString*)detail
|
||||
image:(UIImage*)image
|
||||
duration:(NSTimeInterval)duration
|
||||
andTouchBlock:(MPNotificationSimpleAction)block
|
||||
{
|
||||
if (__notificationWindow == nil)
|
||||
{
|
||||
__notificationWindow = [[MPNotificationWindow alloc] initWithFrame:notificationRect()];
|
||||
__notificationWindow.hidden = NO;
|
||||
}
|
||||
|
||||
MPNotificationView * notification = [[MPNotificationView alloc] initWithFrame:__notificationWindow.bounds];
|
||||
|
||||
notification.textLabel.text = text;
|
||||
notification.detailTextLabel.text = detail;
|
||||
notification.imageView.image = image;
|
||||
notification.duration = duration;
|
||||
notification.tapBlock = block;
|
||||
|
||||
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:notification
|
||||
action:@selector(handleTap:)];
|
||||
notification.tapGestureRecognizer = gr;
|
||||
[notification addGestureRecognizer:gr];
|
||||
|
||||
[__notificationWindow.notificationQueue addObject:notification];
|
||||
|
||||
if (__notificationWindow.currentNotification == nil)
|
||||
{
|
||||
[self showNextNotification];
|
||||
}
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
- (void) handleTap:(UITapGestureRecognizer *)gestureRecognizer
|
||||
{
|
||||
if (_tapBlock != nil)
|
||||
{
|
||||
_tapBlock(self);
|
||||
}
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(tapReceivedForNotificationView:)])
|
||||
{
|
||||
[_delegate didTapOnNotificationView:self];
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMPNotificationViewTapReceivedNotification
|
||||
object:self];
|
||||
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:[self class]
|
||||
selector:@selector(showNextNotification)
|
||||
object:nil];
|
||||
|
||||
[MPNotificationView showNextNotification];
|
||||
}
|
||||
|
||||
+ (void) showNextNotification
|
||||
{
|
||||
UIView *viewToRotateOut = nil;
|
||||
UIImage *screenshot = [self screenImageWithRect:__notificationWindow.frame];
|
||||
|
||||
if (__notificationWindow.currentNotification)
|
||||
{
|
||||
viewToRotateOut = __notificationWindow.currentNotification;
|
||||
}
|
||||
else
|
||||
{
|
||||
viewToRotateOut = [[UIImageView alloc] initWithFrame:__notificationWindow.bounds];
|
||||
((UIImageView *)viewToRotateOut).image = screenshot;
|
||||
[__notificationWindow addSubview:viewToRotateOut];
|
||||
__notificationWindow.hidden = NO;
|
||||
}
|
||||
|
||||
UIView *viewToRotateIn = nil;
|
||||
|
||||
if ([__notificationWindow.notificationQueue count] > 0)
|
||||
{
|
||||
viewToRotateIn = __notificationWindow.notificationQueue[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
viewToRotateIn = [[UIImageView alloc] initWithFrame:__notificationWindow.bounds];
|
||||
((UIImageView *)viewToRotateIn).image = screenshot;
|
||||
}
|
||||
|
||||
viewToRotateIn.layer.anchorPointZ = 11.547f;
|
||||
viewToRotateIn.layer.doubleSided = NO;
|
||||
viewToRotateIn.layer.zPosition = 2;
|
||||
|
||||
CATransform3D viewInStartTransform = CATransform3DMakeRotation(RADIANS(-120), 1.0, 0.0, 0.0);
|
||||
viewInStartTransform.m34 = -1.0 / 200.0;
|
||||
|
||||
viewToRotateOut.layer.anchorPointZ = 11.547f;
|
||||
viewToRotateOut.layer.doubleSided = NO;
|
||||
viewToRotateOut.layer.zPosition = 2;
|
||||
|
||||
CATransform3D viewOutEndTransform = CATransform3DMakeRotation(RADIANS(120), 1.0, 0.0, 0.0);
|
||||
viewOutEndTransform.m34 = -1.0 / 200.0;
|
||||
|
||||
[__notificationWindow addSubview:viewToRotateIn];
|
||||
__notificationWindow.backgroundColor = [UIColor blackColor];
|
||||
|
||||
viewToRotateIn.layer.transform = viewInStartTransform;
|
||||
|
||||
if ([viewToRotateIn isKindOfClass:[MPNotificationView class]] )
|
||||
{
|
||||
UIImageView *bgImage = [[UIImageView alloc] initWithFrame:__notificationWindow.bounds];
|
||||
bgImage.image = screenshot;
|
||||
[viewToRotateIn addSubview:bgImage];
|
||||
[viewToRotateIn sendSubviewToBack:bgImage];
|
||||
__notificationWindow.currentNotification = viewToRotateIn;
|
||||
}
|
||||
|
||||
[UIView animateWithDuration:0.5
|
||||
delay:0.0
|
||||
options:UIViewAnimationOptionCurveEaseInOut
|
||||
animations:^{
|
||||
viewToRotateIn.layer.transform = CATransform3DIdentity;
|
||||
viewToRotateOut.layer.transform = viewOutEndTransform;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
[viewToRotateOut removeFromSuperview];
|
||||
[__notificationWindow.notificationQueue removeObject:viewToRotateOut];
|
||||
if ([viewToRotateIn isKindOfClass:[MPNotificationView class]])
|
||||
{
|
||||
MPNotificationView *notification = (MPNotificationView*)viewToRotateIn;
|
||||
[self performSelector:@selector(showNextNotification)
|
||||
withObject:nil
|
||||
afterDelay:notification.duration];
|
||||
|
||||
__notificationWindow.currentNotification = notification;
|
||||
[__notificationWindow.notificationQueue removeObject:notification];
|
||||
}
|
||||
else
|
||||
{
|
||||
[viewToRotateIn removeFromSuperview];
|
||||
__notificationWindow.hidden = YES;
|
||||
__notificationWindow.currentNotification = nil;
|
||||
}
|
||||
|
||||
__notificationWindow.backgroundColor = [UIColor clearColor];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (UIImage *) screenImageWithRect:(CGRect)rect
|
||||
{
|
||||
CALayer *layer = [[UIApplication sharedApplication] keyWindow].layer;
|
||||
CGFloat scale = [UIScreen mainScreen].scale;
|
||||
UIGraphicsBeginImageContextWithOptions(layer.frame.size, NO, scale);
|
||||
|
||||
[layer renderInContext:UIGraphicsGetCurrentContext()];
|
||||
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
|
||||
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
rect = CGRectMake(rect.origin.x * scale, rect.origin.y * scale
|
||||
, rect.size.width * scale, rect.size.height * scale);
|
||||
|
||||
CGImageRef imageRef = CGImageCreateWithImageInRect([screenshot CGImage], rect);
|
||||
UIImage *croppedScreenshot = [UIImage imageWithCGImage:imageRef
|
||||
scale:screenshot.scale
|
||||
orientation:screenshot.imageOrientation];
|
||||
CGImageRelease(imageRef);
|
||||
|
||||
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
|
||||
UIImageOrientation imageOrientation = UIImageOrientationUp;
|
||||
|
||||
switch (orientation)
|
||||
{
|
||||
case UIDeviceOrientationPortraitUpsideDown:
|
||||
imageOrientation = UIImageOrientationDown;
|
||||
break;
|
||||
case UIDeviceOrientationLandscapeRight:
|
||||
imageOrientation = UIImageOrientationRight;
|
||||
break;
|
||||
case UIDeviceOrientationLandscapeLeft:
|
||||
imageOrientation = UIImageOrientationLeft;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return [[UIImage alloc] initWithCGImage:croppedScreenshot.CGImage
|
||||
scale:croppedScreenshot.scale
|
||||
orientation:imageOrientation];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user