Skip to content
15 changes: 10 additions & 5 deletions ExampleProject/Example/TSDemoViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ - (IBAction)didTapButton:(id)sender
type:TSMessageNotificationTypeSuccess];
}
atPosition:TSMessageNotificationPositionTop
canBeDismissedByUser:YES];
canBeDismissedByUser:YES
textAlign:NSTextAlignmentCenter];
}

- (IBAction)didTapToggleNavigationBar:(id)sender {
Expand All @@ -92,7 +93,8 @@ - (IBAction)didTapCustomImage:(id)sender
buttonTitle:nil
buttonCallback:nil
atPosition:TSMessageNotificationPositionTop
canBeDismissedByUser:YES];
canBeDismissedByUser:YES
textAlign:NSTextAlignmentCenter];
}

- (IBAction)didTapDismissCurrentMessage:(id)sender
Expand All @@ -112,7 +114,8 @@ - (IBAction)didTapEndless:(id)sender
buttonTitle:nil
buttonCallback:nil
atPosition:TSMessageNotificationPositionTop
canBeDismissedByUser:NO];
canBeDismissedByUser:NO
textAlign:NSTextAlignmentCenter];
}

- (IBAction)didTapLong:(id)sender
Expand All @@ -127,7 +130,8 @@ - (IBAction)didTapLong:(id)sender
buttonTitle:nil
buttonCallback:nil
atPosition:TSMessageNotificationPositionTop
canBeDismissedByUser:YES];
canBeDismissedByUser:YES
textAlign:NSTextAlignmentCenter];
}

- (IBAction)didTapBottom:(id)sender
Expand All @@ -142,7 +146,8 @@ - (IBAction)didTapBottom:(id)sender
buttonTitle:nil
buttonCallback:nil
atPosition:TSMessageNotificationPositionBottom
canBeDismissedByUser:YES];
canBeDismissedByUser:YES
textAlign:NSTextAlignmentCenter];
}

- (IBAction)didTapText:(id)sender
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
TSMessages
==========

**Edited** by [Ricardo Pereira](http://twitter.com/ricardopereiraw)
* Add text align support

--------

**Original**

This framework provides an easy to use class to show little notification views on the top of the screen. (à la Tweetbot).

The notification moves from the top of the screen underneath the navigation bar and stays there for a few seconds, depending on the length of the displayed text. To dismiss a notification before the time runs out, the user can swipe it to the top or just tap it.
Expand Down Expand Up @@ -110,4 +117,4 @@ Changes

TODOs
-----
Currently empty
* iOS 7: Blur effect
6 changes: 5 additions & 1 deletion TSMessages/Classes/TSMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef NS_ENUM(NSInteger, TSMessageNotificationType) {
TSMessageNotificationTypeError,
TSMessageNotificationTypeSuccess
};

typedef NS_ENUM(NSInteger, TSMessageNotificationPosition) {
TSMessageNotificationPositionTop = 0,
TSMessageNotificationPositionBottom
Expand Down Expand Up @@ -95,6 +96,7 @@ typedef NS_ENUM(NSInteger,TSMessageNotificationDuration) {
@param buttonCallback The block that should be executed, when the user tapped on the button
@param messagePosition The position of the message on the screen
@param dismissingEnabled Should the message be dismissed when the user taps/swipes it
@param messageAlign The alignment of the message on the screen
*/
+ (void)showNotificationInViewController:(UIViewController *)viewController
title:(NSString *)title
Expand All @@ -106,7 +108,9 @@ typedef NS_ENUM(NSInteger,TSMessageNotificationDuration) {
buttonTitle:(NSString *)buttonTitle
buttonCallback:(void (^)())buttonCallback
atPosition:(TSMessageNotificationPosition)messagePosition
canBeDismissedByUser:(BOOL)dismissingEnabled;
canBeDismissedByUser:(BOOL)dismissingEnabled
textAlign:(NSTextAlignment)messageAlign;


/** Fades out the currently displayed notification. If another notification is in the queue,
the next one will be displayed automatically
Expand Down
22 changes: 16 additions & 6 deletions TSMessages/Classes/TSMessage.m
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ + (void)showNotificationInViewController:(UIViewController *)viewController
buttonTitle:nil
buttonCallback:nil
atPosition:TSMessageNotificationPositionTop
canBeDismissedByUser:YES];
canBeDismissedByUser:YES
textAlign:NSTextAlignmentLeft];
}


Expand All @@ -95,6 +96,7 @@ + (void)showNotificationInViewController:(UIViewController *)viewController
buttonCallback:(void (^)())buttonCallback
atPosition:(TSMessageNotificationPosition)messagePosition
canBeDismissedByUser:(BOOL)dismissingEnabled
textAlign:(NSTextAlignment)messageAlign
{
// Create the TSMessageView
TSMessageView *v = [[TSMessageView alloc] initWithTitle:title
Expand All @@ -107,7 +109,8 @@ + (void)showNotificationInViewController:(UIViewController *)viewController
buttonTitle:buttonTitle
buttonCallback:buttonCallback
atPosition:messagePosition
canBeDismissedByUser:dismissingEnabled];
canBeDismissedByUser:dismissingEnabled
textAlign:messageAlign];
[self prepareNotificationToBeShown:v];
}

Expand Down Expand Up @@ -170,7 +173,7 @@ - (void)fadeInCurrentNotification
currentNavigationController = (UINavigationController *)currentView.viewController;
else
currentNavigationController = (UINavigationController *)currentView.viewController.parentViewController;

BOOL isViewIsUnderStatusBar = [[[currentNavigationController childViewControllers] firstObject] wantsFullScreenLayout];
if (!isViewIsUnderStatusBar && currentNavigationController.parentViewController == nil) {
isViewIsUnderStatusBar = ![currentNavigationController isNavigationBarHidden]; // strange but true
Expand All @@ -179,7 +182,14 @@ - (void)fadeInCurrentNotification
{
[currentNavigationController.view insertSubview:currentView
belowSubview:[currentNavigationController navigationBar]];
verticalOffset = [currentNavigationController navigationBar].bounds.size.height;

NSArray *titleViewsBounds = [currentView.viewController.navigationController.viewControllers valueForKeyPath:@"navigationItem.titleView.bounds"];
__block CGFloat maxTitleViewHeight = 0.0;
[titleViewsBounds enumerateObjectsUsingBlock:^(NSValue *boundsValue, NSUInteger idx, BOOL *stop) {
CGRect rect = [boundsValue isEqual:NSNull.null] ? CGRectZero : boundsValue.CGRectValue;
maxTitleViewHeight = CGRectGetHeight(rect) > maxTitleViewHeight ? CGRectGetHeight(rect) : maxTitleViewHeight;
}];
verticalOffset = MAX([currentNavigationController navigationBar].bounds.size.height, maxTitleViewHeight);
if ([TSMessage iOS7StyleEnabled] || isViewIsUnderStatusBar) {
addStatusBarHeightToVerticalOffset();
}
Expand Down Expand Up @@ -219,7 +229,7 @@ - (void)fadeInCurrentNotification
}
toPoint = CGPointMake(currentView.center.x, y);
}

dispatch_block_t animationBlock = ^{
currentView.center = toPoint;
if (![TSMessage iOS7StyleEnabled]) {
Expand Down Expand Up @@ -361,7 +371,7 @@ + (BOOL)iOS7StyleEnabled
// Decide wheter to use iOS 7 style or not based on the running device and the base sdk
BOOL iOS7SDK = NO;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
iOS7SDK = YES;
iOS7SDK = YES;
#endif

_useiOS7Style = ! (TS_SYSTEM_VERSION_LESS_THAN(@"7.0") || !iOS7SDK);
Expand Down
8 changes: 7 additions & 1 deletion TSMessages/Views/TSMessageView.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
/** The position of the message (top or bottom) */
@property (nonatomic, assign) TSMessageNotificationPosition messagePosition;

/** The alignment of the message (left, center, right, justified, natural) */
@property (nonatomic, assign) NSTextAlignment messageAlignment;

/** Is the message currenlty fully displayed? Is set as soon as the message is really fully visible */
@property (nonatomic, assign) BOOL messageIsFullyDisplayed;

Expand All @@ -57,6 +60,7 @@
@param buttonCallback The block that should be executed, when the user tapped on the button
@param position The position of the message on the screen
@param dismissingEnabled Should this message be dismissed when the user taps/swipes it?
@param textAlign The align of the message on the screen
*/
- (id)initWithTitle:(NSString *)title
subtitle:(NSString *)subtitle
Expand All @@ -68,7 +72,9 @@
buttonTitle:(NSString *)buttonTitle
buttonCallback:(void (^)())buttonCallback
atPosition:(TSMessageNotificationPosition)position
canBeDismissedByUser:(BOOL)dismissingEnabled;
canBeDismissedByUser:(BOOL)dismissingEnabled
textAlign:(NSTextAlignment)alignment;


/** Fades out this notification view */
- (void)fadeMeOut;
Expand Down
76 changes: 56 additions & 20 deletions TSMessages/Views/TSMessageView.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
//

#import "TSMessageView.h"
#import "HexColor.h"
#import "TSBlurView.h"
#import "HexColors.h"
#import "TSMessage.h"


Expand Down Expand Up @@ -45,7 +44,7 @@ @interface TSMessageView () <UIGestureRecognizerDelegate>
@property (nonatomic, strong) UIButton *button;
@property (nonatomic, strong) UIView *borderView;
@property (nonatomic, strong) UIImageView *backgroundImageView;
@property (nonatomic, strong) TSBlurView *backgroundBlurView; // Only used in iOS 7
@property (nonatomic, strong) UIView *backgroundView; // Only used in iOS 7

@property (nonatomic, assign) CGFloat textSpaceLeft;
@property (nonatomic, assign) CGFloat textSpaceRight;
Expand All @@ -67,8 +66,8 @@ + (NSMutableDictionary *)notificationDesign
{
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:TSDesignFileName];
_notificationDesign = [NSMutableDictionary dictionaryWithDictionary:[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:path]
options:kNilOptions
error:nil]];
options:kNilOptions
error:nil]];
}

return _notificationDesign;
Expand All @@ -79,8 +78,8 @@ + (void)addNotificationDesignFromFile:(NSString *)filename
{
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:filename];
NSDictionary *design = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:path]
options:kNilOptions
error:nil];
options:kNilOptions
error:nil];

[[TSMessageView notificationDesign] addEntriesFromDictionary:design];
}
Expand All @@ -96,6 +95,7 @@ - (id)initWithTitle:(NSString *)title
buttonCallback:(void (^)())buttonCallback
atPosition:(TSMessageNotificationPosition)position
canBeDismissedByUser:(BOOL)dismissingEnabled
textAlign:(NSTextAlignment)alignment
{
NSDictionary *notificationDesign = [TSMessageView notificationDesign];

Expand All @@ -107,6 +107,7 @@ - (id)initWithTitle:(NSString *)title
_duration = duration;
_viewController = viewController;
_messagePosition = position;
_messageAlignment = alignment;
self.callback = callback;
self.buttonCallback = buttonCallback;

Expand Down Expand Up @@ -141,7 +142,7 @@ - (id)initWithTitle:(NSString *)title
}

current = [notificationDesign valueForKey:currentString];


if (!image && [current valueForKey:@"imageName"])
{
Expand All @@ -160,11 +161,10 @@ - (id)initWithTitle:(NSString *)title
}
else
{
// On iOS 7 and above use a blur layer instead (not yet finished)
_backgroundBlurView = [[TSBlurView alloc] init];
self.backgroundBlurView.autoresizingMask = (UIViewAutoresizingFlexibleWidth);
self.backgroundBlurView.blurTintColor = [UIColor colorWithHexString:current[@"backgroundColor"]];
[self addSubview:self.backgroundBlurView];
_backgroundView = [[UIView alloc] init];
self.backgroundView.autoresizingMask = (UIViewAutoresizingFlexibleWidth);
self.backgroundView.backgroundColor = [UIColor colorWithHexString:current[@"backgroundColor"] alpha:current[@"backgroundAlpha"] ? [current[@"backgroundAlpha"] floatValue] : 1.0];
[self addSubview:self.backgroundView];
}

UIColor *fontColor = [UIColor colorWithHexString:[current valueForKey:@"textColor"]
Expand All @@ -191,6 +191,13 @@ - (id)initWithTitle:(NSString *)title
[[current valueForKey:@"shadowOffsetY"] floatValue])];
self.titleLabel.numberOfLines = 0;
self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
// Check alignment: layout has priority!
int align = [[current valueForKey:@"textAlignment"] intValue];
if (align > 0)
self.titleLabel.textAlignment = align;
else
self.titleLabel.textAlignment = self.messageAlignment;

[self addSubview:self.titleLabel];

// Set up content label (if set)
Expand All @@ -217,7 +224,12 @@ - (id)initWithTitle:(NSString *)title
[self.contentLabel setShadowOffset:self.titleLabel.shadowOffset];
self.contentLabel.lineBreakMode = self.titleLabel.lineBreakMode;
self.contentLabel.numberOfLines = 0;

// Check alignment: layout has priority!
if (align > 0)
self.contentLabel.textAlignment = align;
else
self.contentLabel.textAlignment = self.messageAlignment;

[self addSubview:self.contentLabel];
}

Expand Down Expand Up @@ -347,6 +359,11 @@ - (CGFloat)updateHeightOfMessageView
0.0);
[self.titleLabel sizeToFit];

// for title text alignment
CGRect currFrame = self.titleLabel.frame;

self.titleLabel.frame = CGRectMake(currFrame.origin.x, currFrame.origin.y, screenWidth - TSMessageViewPadding - self.iconImageView.frame.origin.x - self.iconImageView.frame.size.width - TSMessageViewPadding - self.textSpaceLeft - self.textSpaceRight, currFrame.size.height);

if ([self.subtitle length])
{
self.contentLabel.frame = CGRectMake(self.textSpaceLeft,
Expand All @@ -355,6 +372,11 @@ - (CGFloat)updateHeightOfMessageView
0.0);
[self.contentLabel sizeToFit];

// for content text alignment
currFrame = self.contentLabel.frame;

self.contentLabel.frame = CGRectMake(currFrame.origin.x, currFrame.origin.y, screenWidth - TSMessageViewPadding - self.iconImageView.frame.origin.x - self.iconImageView.frame.size.width - TSMessageViewPadding - self.textSpaceLeft - self.textSpaceRight, currFrame.size.height);

currentHeight = self.contentLabel.frame.origin.y + self.contentLabel.frame.size.height;
}
else
Expand Down Expand Up @@ -404,20 +426,34 @@ - (CGFloat)updateHeightOfMessageView
self.button.frame.size.width,
self.button.frame.size.height);
}


NSArray *titleViewsBounds = [self.viewController.navigationController.viewControllers valueForKeyPath:@"navigationItem.titleView.bounds"];
__block CGFloat maxTitleViewHeight = 0.0;
CGFloat differenceHeight = 0.0;
[titleViewsBounds enumerateObjectsUsingBlock:^(NSValue *boundsValue, NSUInteger idx, BOOL *stop) {
CGRect rect = [boundsValue isEqual:NSNull.null] ? CGRectZero : boundsValue.CGRectValue;
maxTitleViewHeight = CGRectGetHeight(rect) > maxTitleViewHeight ? CGRectGetHeight(rect) : maxTitleViewHeight;
}];


if (maxTitleViewHeight > currentHeight)
{
differenceHeight = maxTitleViewHeight - currentHeight;
currentHeight += differenceHeight;
}

CGRect backgroundFrame = CGRectMake(self.backgroundImageView.frame.origin.x,
self.backgroundImageView.frame.origin.y,
MIN(self.backgroundImageView.frame.origin.y, -differenceHeight),
screenWidth,
currentHeight);

// increase frame of background view because of the spring animation
if ([TSMessage iOS7StyleEnabled])
{
if (self.messagePosition == TSMessageNotificationPositionTop)
{
float topOffset = 0.f;

UINavigationController *navigationController = self.viewController.navigationController;
if (!navigationController && [self.viewController isKindOfClass:[UINavigationController class]]) {
navigationController = (UINavigationController *)self.viewController;
Expand All @@ -435,9 +471,9 @@ - (CGFloat)updateHeightOfMessageView
backgroundFrame = UIEdgeInsetsInsetRect(backgroundFrame, UIEdgeInsetsMake(0.f, 0.f, -30.f, 0.f));
}
}

self.backgroundImageView.frame = backgroundFrame;
self.backgroundBlurView.frame = backgroundFrame;
self.backgroundView.frame = backgroundFrame;

return currentHeight;
}
Expand Down