Skip to content

Commit

Permalink
fixes for iOS7. should fix #1
Browse files Browse the repository at this point in the history
There was a lot more to do than I originally anticipated to get this
working for 7, but I think things are good now. All three positions
seem to work appropriately on iOS versions 5-7
  • Loading branch information
lobianco committed Aug 26, 2013
1 parent 634c7de commit c0240eb
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 36 deletions.
16 changes: 12 additions & 4 deletions ALAlertBanner/ALAlertBannerManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ -(id)init
dispatch_semaphore_signal(_navBarPositionSemaphore);

_bannerViews = [NSMutableArray new];

_secondsToShow = 3.5f;
_showAnimationDuration = 0.25f;
_hideAnimationDuration = 0.2f;
Expand Down Expand Up @@ -178,14 +179,19 @@ -(void)hideAlertBanner:(ALAlertBannerView *)alertBanner

-(void)alertBannerWillShow:(ALAlertBannerView *)alertBanner inView:(UIView*)view
{
//make copy so we can set shadow before pushing banners
NSArray *bannersToPush = [NSArray arrayWithArray:view.alertBanners];
NSMutableArray *bannersArray = view.alertBanners;
for (ALAlertBannerView *banner in bannersArray)
if (banner.position == alertBanner.position)
[banner push:alertBanner.frame.size.height forward:YES];

[bannersArray addObject:alertBanner];
NSArray *bannersInSamePosition = [bannersArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF.position == %i", alertBanner.position]];

//set shadow before pushing other banners, because the banner push may be delayed by the fade in duration (which is set at the same time as the shadow) on iOS7
alertBanner.showShadow = (bannersInSamePosition.count > 1 ? NO : YES);

for (ALAlertBannerView *banner in bannersToPush)
if (banner.position == alertBanner.position)
[banner push:alertBanner.frame.size.height forward:YES delay:alertBanner.fadeInDuration];
}

-(void)alertBannerDidShow:(ALAlertBannerView *)alertBanner inView:(UIView *)view
Expand Down Expand Up @@ -215,7 +221,7 @@ -(void)alertBannerWillHide:(ALAlertBannerView *)alertBanner inView:(UIView *)vie
NSArray *bannersToPush = [bannersInSamePosition subarrayWithRange:NSMakeRange(0, index)];

for (ALAlertBannerView *banner in bannersToPush)
[banner push:-alertBanner.frame.size.height forward:NO];
[banner push:-alertBanner.frame.size.height forward:NO delay:0.f];
}

else if (index == 0)
Expand Down Expand Up @@ -285,6 +291,8 @@ -(void)didRotate:(NSNotification *)note
{
NSArray *topBanners = [view.alertBanners filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF.position == %i", ALAlertBannerPositionTop]];
CGFloat topYCoord = 0.f;
if (AL_IOS_7_OR_GREATER)
topYCoord += [UIApplication navigationBarHeight] + kStatusBarHeight;
for (ALAlertBannerView *alertBanner in [topBanners reverseObjectEnumerator])
{
[alertBanner updateSizeAndSubviewsAnimated:YES];
Expand Down
21 changes: 18 additions & 3 deletions ALAlertBanner/ALAlertBannerView.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@

#import <UIKit/UIKit.h>

/**
include these constants and category methods here so Manager can see them
*/

#define AL_IOS_7_OR_GREATER [UIDevice iOSVersion] >= 7.0

static CGFloat const kStatusBarHeight = 20.f;

@interface UIDevice (SystemVersion)
+(float)iOSVersion;
@end

@interface UIApplication (NavigationBarHeight)
+(CGFloat)navigationBarHeight;
@end

typedef enum {
ALAlertBannerStyleSuccess = 0,
ALAlertBannerStyleFailure,
Expand Down Expand Up @@ -75,6 +91,7 @@ typedef enum {
@property (nonatomic) BOOL isScheduledToHide;
@property (nonatomic) BOOL allowTapToDismiss;

@property (nonatomic) NSTimeInterval fadeInDuration;
@property (nonatomic) BOOL showShadow;
@property (nonatomic) NSTimeInterval showAnimationDuration;
@property (nonatomic) NSTimeInterval hideAnimationDuration;
Expand All @@ -83,10 +100,8 @@ typedef enum {
+(ALAlertBannerView*)alertBannerForView:(UIView*)view style:(ALAlertBannerStyle)style position:(ALAlertBannerPosition)position title:(NSString*)title subtitle:(NSString*)subtitle;
-(void)show;
-(void)hide;
-(void)push:(CGFloat)distance forward:(BOOL)forward;
-(void)push:(CGFloat)distance forward:(BOOL)forward delay:(double)delay;
-(void)updateSizeAndSubviewsAnimated:(BOOL)animated;
-(void)updatePositionAfterRotationWithY:(CGFloat)yPos animated:(BOOL)animated;

@end


109 changes: 81 additions & 28 deletions ALAlertBanner/ALAlertBannerView.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,35 @@ this software and associated documentation files (the "Software"), to deal in
static NSString * const kHideAlertBannerKey = @"hideAlertBannerKey";
static NSString * const kMoveAlertBannerKey = @"moveAlertBannerKey";
static CGFloat const kMargin = 10.f;
static CGFloat const kNavigationBarHeight = 44.f;
static CGFloat const kStatusBarHeight = 20.f;

static CGFloat const kNavigationBarHeightDefault = 44.f;
static CGFloat const kNavigationBarHeightiOS7Landscape = 32.f;
static CGFloat const kRotationDurationIphone = 0.3f;
static CGFloat const kRotationDurationIPad = 0.4f;

#define DEVICE_ANIMATION_DURATION UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? kRotationDurationIPad : kRotationDurationIphone;
#define AL_DEVICE_ANIMATION_DURATION UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? kRotationDurationIPad : kRotationDurationIphone;

//macros referenced from MBProgressHUD. cheers to @matej
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
#define AL_SINGLELINE_TEXT_HEIGHT(text, font) [text length] > 0 ? [text sizeWithAttributes:nil].height : 0.f;
#else
#define AL_SINGLELINE_TEXT_HEIGHT(text, font) [text length] > 0 ? [text sizeWithFont:font].height : 0.f;
#endif

#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
#define AL_MULTILINE_TEXT_HEIGHT(text, font, maxSize, mode) [text length] > 0 ? [text boundingRectWithSize:maxSize \
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) \
attributes:nil \
context:NULL].size.height : 0.f;
#else
#define AL_MULTILINE_TEXT_HEIGHT(text, font, maxSize, mode) [text length] > 0 ? [text sizeWithFont:font \
constrainedToSize:maxSize \
lineBreakMode:mode].height : 0.f;
#endif

# pragma mark -
# pragma mark Helper Categories

//referenced from http://stackoverflow.com/questions/11598043/get-slightly-lighter-and-darker-color-from-uicolor
//darkerColor referenced from http://stackoverflow.com/questions/11598043/get-slightly-lighter-and-darker-color-from-uicolor
@implementation UIColor (LightAndDark)
- (UIColor *)darkerColor
{
Expand All @@ -54,13 +71,35 @@ - (UIColor *)darkerColor
}
@end

@implementation UIDevice (SystemVersion)
+(float)iOSVersion
{
static float version = 0;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
version = [[[UIDevice currentDevice] systemVersion] floatValue];
});
return version;
}
@end

@implementation UIApplication (NavigationBarHeight)
+(CGFloat)navigationBarHeight
{
//if we're on iOS7 or later, return new landscape navBar height
if (AL_IOS_7_OR_GREATER && UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]))
return kNavigationBarHeightiOS7Landscape;

return kNavigationBarHeightDefault;
}
@end

@interface ALAlertBannerView ()

@property (nonatomic, assign) ALAlertBannerStyle style;
@property (nonatomic, assign) ALAlertBannerPosition position;
@property (nonatomic, assign) ALAlertBannerState state;

@property (nonatomic) NSTimeInterval fadeInDuration;
@property (nonatomic) NSTimeInterval fadeOutDuration;

@property (nonatomic, readonly) BOOL isAnimating;
Expand All @@ -87,6 +126,7 @@ - (id)init
return self;
}

# pragma mark -
# pragma mark Initializer Helpers

-(void)commonInit
Expand Down Expand Up @@ -126,6 +166,7 @@ -(void)commonInit
[self addSubview:_subtitleLabel];
}

# pragma mark -
# pragma mark Custom Setters & Getters

-(void)setStyle:(ALAlertBannerStyle)style
Expand Down Expand Up @@ -179,7 +220,9 @@ -(void)setShowShadow:(BOOL)showShadow
newShadowRadius = 0.f;
self.layer.shadowRadius = 0.f;
self.layer.shadowOffset = CGSizeZero;
self.fadeInDuration = 0.f;

//if on iOS7, keep fade in duration at a value greater than 0 so it doesn't instantly appear behind the translucent nav bar
self.fadeInDuration = (AL_IOS_7_OR_GREATER && self.position == ALAlertBannerPositionTop) ? 0.15f : 0.f;
}

self.layer.shouldRasterize = YES;
Expand All @@ -201,6 +244,7 @@ -(BOOL)isAnimating
self.state == ALAlertBannerStateMovingBackward);
}

# pragma mark -
# pragma mark Class Methods

+(ALAlertBannerView*)alertBannerForView:(UIView*)view style:(ALAlertBannerStyle)style position:(ALAlertBannerPosition)position title:(NSString*)title subtitle:(NSString*)subtitle
Expand All @@ -226,6 +270,7 @@ +(ALAlertBannerView*)alertBannerForView:(UIView*)view style:(ALAlertBannerStyle)
return alertBanner;
}

# pragma mark -
# pragma mark Instance Methods

-(void)show
Expand Down Expand Up @@ -284,7 +329,7 @@ -(void)show
moveLayer.delegate = self;
[moveLayer setValue:kShowAlertBannerKey forKey:@"anim"];

[self.layer addAnimation:moveLayer forKey:@"position"];
[self.layer addAnimation:moveLayer forKey:kShowAlertBannerKey];
});

[UIView animateWithDuration:self.fadeInDuration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
Expand Down Expand Up @@ -337,10 +382,10 @@ -(void)hide
moveLayer.delegate = self;
[moveLayer setValue:kHideAlertBannerKey forKey:@"anim"];

[self.layer addAnimation:moveLayer forKey:@"position"];
[self.layer addAnimation:moveLayer forKey:kHideAlertBannerKey];
}

-(void)push:(CGFloat)distance forward:(BOOL)forward
-(void)push:(CGFloat)distance forward:(BOOL)forward delay:(double)delay
{
self.state = (forward ? ALAlertBannerStateMovingForward : ALAlertBannerStateMovingBackward);

Expand All @@ -353,19 +398,24 @@ -(void)push:(CGFloat)distance forward:(BOOL)forward
CGPoint oldPoint = activeLayer.position;
CGPoint newPoint = CGPointMake(oldPoint.x, (self.layer.position.y - oldPoint.y)+oldPoint.y+distanceToPush);

self.layer.position = newPoint;

CABasicAnimation *moveLayer = [CABasicAnimation animationWithKeyPath:@"position"];
moveLayer.fromValue = [NSValue valueWithCGPoint:oldPoint];
moveLayer.toValue = [NSValue valueWithCGPoint:newPoint];
moveLayer.duration = forward ? self.showAnimationDuration : self.hideAnimationDuration;
moveLayer.timingFunction = forward ? [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut] : [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
moveLayer.delegate = self;
[moveLayer setValue:kMoveAlertBannerKey forKey:@"anim"];

[self.layer addAnimation:moveLayer forKey:@"position"];
double delayInSeconds = delay;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
self.layer.position = newPoint;

CABasicAnimation *moveLayer = [CABasicAnimation animationWithKeyPath:@"position"];
moveLayer.fromValue = [NSValue valueWithCGPoint:oldPoint];
moveLayer.toValue = [NSValue valueWithCGPoint:newPoint];
moveLayer.duration = forward ? self.showAnimationDuration : self.hideAnimationDuration;
moveLayer.timingFunction = forward ? [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut] : [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
moveLayer.delegate = self;
[moveLayer setValue:kMoveAlertBannerKey forKey:@"anim"];

[self.layer addAnimation:moveLayer forKey:kMoveAlertBannerKey];
});
}

# pragma mark -
# pragma mark Touch Recognition

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Expand All @@ -374,6 +424,7 @@ -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
[self.delegate hideAlertBanner:self];
}

# pragma mark -
# pragma mark Private Methods

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
Expand Down Expand Up @@ -416,8 +467,8 @@ -(void)setInitialLayout
BOOL isSuperviewKindOfWindow = ([parentView isKindOfClass:[UIWindow class]]);

CGSize maxLabelSize = CGSizeMake(parentView.bounds.size.width - (kMargin*3) - self.statusImageView.image.size.width, CGFLOAT_MAX);
CGFloat titleLabelHeight = self.titleLabel.font.pointSize + 2.f;
CGFloat subtitleLabelHeight = [self.subtitleLabel.text sizeWithFont:self.subtitleLabel.font constrainedToSize:maxLabelSize lineBreakMode:self.subtitleLabel.lineBreakMode].height;
CGFloat titleLabelHeight = AL_SINGLELINE_TEXT_HEIGHT(self.titleLabel.text, self.titleLabel.font);
CGFloat subtitleLabelHeight = AL_MULTILINE_TEXT_HEIGHT(self.subtitleLabel.text, self.subtitleLabel.font, maxLabelSize, self.subtitleLabel.lineBreakMode);
CGFloat heightForSelf = titleLabelHeight + subtitleLabelHeight + (self.subtitleLabel.text == nil ? kMargin*2 : kMargin*2.5);

CGRect frame = CGRectMake(0, 0, parentView.bounds.size.width, heightForSelf);
Expand All @@ -426,12 +477,14 @@ -(void)setInitialLayout
case ALAlertBannerPositionTop:
initialYCoord = -heightForSelf;
if (isSuperviewKindOfWindow) initialYCoord += kStatusBarHeight;
if (AL_IOS_7_OR_GREATER)
initialYCoord += [UIApplication navigationBarHeight] + kStatusBarHeight;
break;
case ALAlertBannerPositionBottom:
initialYCoord = parentView.bounds.size.height;
break;
case ALAlertBannerPositionUnderNavBar:
initialYCoord = -heightForSelf + kNavigationBarHeight + kStatusBarHeight;
initialYCoord = -heightForSelf + [UIApplication navigationBarHeight] + kStatusBarHeight;
break;
}
frame.origin.y = initialYCoord;
Expand All @@ -454,11 +507,11 @@ -(void)setInitialLayout
-(void)updateSizeAndSubviewsAnimated:(BOOL)animated
{
CGSize maxLabelSize = CGSizeMake(self.parentView.bounds.size.width - (kMargin*3) - self.statusImageView.image.size.width, CGFLOAT_MAX);
CGFloat titleLabelHeight = self.titleLabel.font.pointSize + 2.f;
CGFloat subtitleLabelHeight = [self.subtitleLabel.text sizeWithFont:self.subtitleLabel.font constrainedToSize:maxLabelSize lineBreakMode:self.subtitleLabel.lineBreakMode].height;
CGFloat titleLabelHeight = AL_SINGLELINE_TEXT_HEIGHT(self.titleLabel.text, self.titleLabel.font);
CGFloat subtitleLabelHeight = AL_MULTILINE_TEXT_HEIGHT(self.subtitleLabel.text, self.subtitleLabel.font, maxLabelSize, self.subtitleLabel.lineBreakMode);
CGFloat heightForSelf = titleLabelHeight + subtitleLabelHeight + (self.subtitleLabel.text == nil ? kMargin*2 : kMargin*2.5);

CFTimeInterval boundsAnimationDuration = DEVICE_ANIMATION_DURATION;
CFTimeInterval boundsAnimationDuration = AL_DEVICE_ANIMATION_DURATION;

CGRect oldBounds = self.layer.bounds;
CGRect newBounds = oldBounds;
Expand Down Expand Up @@ -560,7 +613,7 @@ -(void)updatePositionAfterRotationWithY:(CGFloat)yPos animated:(BOOL)animated

//because the banner's location is relative to the height of the screen when in the bottom position, we should just immediately set it's position upon rotation events. this will prevent any ill-timed animations due to the presentation layer's position at the time of rotation
if (self.position == ALAlertBannerPositionBottom)
positionAnimationDuration = DEVICE_ANIMATION_DURATION;
positionAnimationDuration = AL_DEVICE_ANIMATION_DURATION;

positionAnimation.duration = positionAnimationDuration;
positionAnimation.timingFunction = timingFunction == nil ? [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear] : timingFunction;
Expand Down
2 changes: 2 additions & 0 deletions ALAlertBannerDemo/ALAlertBannerDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "ALAlertBannerDemo/ALAlertBannerDemo-Prefix.pch";
INFOPLIST_FILE = "ALAlertBannerDemo/ALAlertBannerDemo-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
Expand All @@ -331,6 +332,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "ALAlertBannerDemo/ALAlertBannerDemo-Prefix.pch";
INFOPLIST_FILE = "ALAlertBannerDemo/ALAlertBannerDemo-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
Expand Down
2 changes: 1 addition & 1 deletion ALAlertBannerDemo/ALAlertBannerDemo/ViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ - (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.

self.view.backgroundColor = [UIColor colorWithRed:243/255.0 green:247/255.0 blue:249/255.0 alpha:1.f];

self.topButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
Expand Down

0 comments on commit c0240eb

Please sign in to comment.