master
  1//
  2//  TSMessage.m
  3//  Toursprung
  4//
  5//  Created by Felix Krause on 24.08.12.
  6//  Copyright (c) 2012 Toursprung. All rights reserved.
  7//
  8
  9#import "TSMessage.h"
 10#import "TSMessageView.h"
 11
 12#define kTSMessageDisplayTime 1.5
 13#define kTSMessageExtraDisplayTimePerPixel 0.04
 14#define kTSMessageAnimationDuration 0.3
 15
 16@interface TSMessage ()
 17
 18/** The queued messages (TSMessageView objects) */
 19@property (nonatomic, strong) NSMutableArray *messages;
 20
 21- (void)fadeInCurrentNotification;
 22- (void)fadeOutNotification:(TSMessageView *)currentView;
 23
 24@end
 25
 26@implementation TSMessage
 27
 28static TSMessage *sharedMessages;
 29static BOOL notificationActive;
 30
 31
 32+ (TSMessage *)sharedMessage
 33{
 34    if (!sharedMessages)
 35    {
 36        sharedMessages = [[[self class] alloc] init];
 37    }
 38    return sharedMessages;
 39}
 40
 41+ (BOOL)isNotificationActive
 42{
 43    return notificationActive;
 44}
 45
 46#pragma mark Methods to call from outside
 47
 48+ (void)showNotificationWithMessage:(NSString *)message
 49                           withType:(TSMessageNotificationType)type
 50{
 51    [self showNotificationWithTitle:message withMessage:nil withType:type];
 52}
 53
 54+ (void)showNotificationWithTitle:(NSString *)title
 55                      withMessage:(NSString *)message
 56                         withType:(TSMessageNotificationType)type
 57{
 58    [self showNotificationInViewController:[self defaultViewController]
 59                                 withTitle:title
 60                               withMessage:message
 61                                  withType:type];
 62}
 63
 64+ (void)showNotificationInViewController:(UIViewController *)viewController
 65                               withTitle:(NSString *)title
 66                             withMessage:(NSString *)message
 67                                withType:(TSMessageNotificationType)type
 68{
 69    [self showNotificationInViewController:viewController
 70                                 withTitle:title
 71                               withMessage:message
 72                                  withType:type
 73                              withDuration:0.0];
 74}
 75
 76+ (void)showNotificationInViewController:(UIViewController *)viewController
 77                               withTitle:(NSString *)title
 78                             withMessage:(NSString *)message
 79                                withType:(TSMessageNotificationType)type
 80                            withDuration:(NSTimeInterval)duration
 81{
 82    [self showNotificationInViewController:viewController
 83                                 withTitle:title
 84                               withMessage:message
 85                                  withType:type
 86                              withDuration:duration
 87                              withCallback:nil];
 88}
 89
 90+ (void)showNotificationInViewController:(UIViewController *)viewController
 91                               withTitle:(NSString *)title
 92                             withMessage:(NSString *)message
 93                                withType:(TSMessageNotificationType)type
 94                            withDuration:(NSTimeInterval)duration
 95                            withCallback:(void (^)())callback
 96{
 97    [self showNotificationInViewController:viewController
 98                                 withTitle:title
 99                               withMessage:message
100                                  withType:type
101                              withDuration:duration
102                              withCallback:callback
103                                atPosition:TSMessageNotificationPositionTop];
104}
105
106+ (void)showNotificationInViewController:(UIViewController *)viewController
107                               withTitle:(NSString *)title
108                             withMessage:(NSString *)message
109                                withType:(TSMessageNotificationType)type
110                            withDuration:(NSTimeInterval)duration
111                            withCallback:(void (^)())callback
112                              atPosition:(TSMessageNotificationPosition)messagePosition
113{
114    [self showNotificationInViewController:viewController
115                                 withTitle:title
116                               withMessage:message
117                                  withType:type
118                              withDuration:duration
119                              withCallback:callback
120                           withButtonTitle:nil
121                        withButtonCallback:nil
122                                atPosition:messagePosition
123                       canBeDismisedByUser:YES];
124}
125
126
127+ (void)showNotificationInViewController:(UIViewController *)viewController
128                               withTitle:(NSString *)title
129                             withMessage:(NSString *)message
130                                withType:(TSMessageNotificationType)type
131                            withDuration:(NSTimeInterval)duration
132                            withCallback:(void (^)())callback
133                         withButtonTitle:(NSString *)buttonTitle
134                      withButtonCallback:(void (^)())buttonCallback
135                              atPosition:(TSMessageNotificationPosition)messagePosition
136                     canBeDismisedByUser:(BOOL)dismissingEnabled
137{
138    for (TSMessageView *n in [TSMessage sharedMessage].messages)
139    {
140        if (([n.title isEqualToString:title] || (!n.title && !title)) && ([n.content isEqualToString:message] || (!n.content && !message)))
141        {
142            return; // avoid showing the same messages twice in a row
143        }
144    }
145    
146    // Create the TSMessageView
147    TSMessageView *v = [[TSMessageView alloc] initWithTitle:title
148                                                withContent:message
149                                                   withType:type
150                                               withDuration:duration
151                                           inViewController:viewController
152                                               withCallback:callback
153                                            withButtonTitle:buttonTitle
154                                         withButtonCallback:buttonCallback
155                                                 atPosition:messagePosition
156                                          shouldBeDismissed:dismissingEnabled];
157    
158    [[TSMessage sharedMessage].messages addObject:v];
159    
160    if (!notificationActive)
161    {
162        [[TSMessage sharedMessage] fadeInCurrentNotification];
163    }
164}
165
166#pragma mark Example uses
167
168+ (void)showInternetError
169{
170    [TSMessage showNotificationWithTitle:NSLocalizedString(@"Network error", nil)
171                             withMessage:NSLocalizedString(@"Couldn't connect to the server. Check your network connection.", nil)
172                                withType:TSMessageNotificationTypeError];
173}
174
175+ (void)showLocationError
176{
177    [TSMessage showNotificationWithTitle:NSLocalizedString(@"Location error", nil)
178                             withMessage:NSLocalizedString(@"Couldn't detect your current location.", nil)
179                                withType:TSMessageNotificationTypeError];
180}
181
182
183#pragma mark Setting up of notification views
184
185- (id)init
186{
187    if ((self = [super init]))
188    {
189        _messages = [[NSMutableArray alloc] init];
190    }
191    return self;
192}
193
194- (void)fadeInCurrentNotification
195{
196    if ([self.messages count] == 0) return;
197    
198    notificationActive = YES;
199    
200    TSMessageView *currentView = [self.messages objectAtIndex:0];
201    
202    CGFloat verticalOffset = 0.0f;
203    
204    if ([currentView.viewController isKindOfClass:[UINavigationController class]])
205    {
206        if (![(UINavigationController *)currentView.viewController isNavigationBarHidden])
207        {
208            [currentView.viewController.view insertSubview:currentView
209                                              belowSubview:[(UINavigationController *)currentView.viewController navigationBar]];
210            verticalOffset = [(UINavigationController *)currentView.viewController navigationBar].bounds.size.height;
211            
212            if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]))
213            {
214                verticalOffset += [UIApplication sharedApplication].statusBarFrame.size.height;
215            }
216            else
217            {
218                verticalOffset += [UIApplication sharedApplication].statusBarFrame.size.width;
219            }
220        }
221        else
222        {
223            [currentView.viewController.view addSubview:currentView];
224        }
225    }
226    else
227    {
228        [currentView.viewController.view addSubview:currentView];
229    }
230    
231    CGPoint toPoint;
232    if (currentView.messagePosition == TSMessageNotificationPositionTop)
233    {
234        toPoint = CGPointMake(currentView.center.x,
235                              [[self class] navigationbarBottomOfViewController:currentView.viewController] + verticalOffset + CGRectGetHeight(currentView.frame) / 2.0);
236    }
237    else
238    {
239        toPoint = CGPointMake(currentView.center.x,
240                              currentView.viewController.view.bounds.size.height - CGRectGetHeight(currentView.frame) / 2.0);
241    }
242    
243    [UIView animateWithDuration:kTSMessageAnimationDuration animations:^
244     {
245         currentView.center = toPoint;
246         currentView.alpha = TSMessageViewAlpha;
247     } completion:^(BOOL finished) {
248         currentView.messageIsFullyDisplayed = YES;
249     }];
250    
251    
252    if (currentView.duration == TSMessageNotificationDurationAutomatic)
253    {
254        currentView.duration = kTSMessageAnimationDuration + kTSMessageDisplayTime + currentView.frame.size.height * kTSMessageExtraDisplayTimePerPixel;
255    }
256    
257    if (currentView.duration != TSMessageNotificationDurationEndless)
258    {
259        dispatch_async(dispatch_get_main_queue(), ^
260        {
261            [self performSelector:@selector(fadeOutNotification:)
262                       withObject:currentView
263                       afterDelay:currentView.duration];
264        });
265    }
266}
267
268- (void)fadeOutNotification:(TSMessageView *)currentView
269{
270    currentView.messageIsFullyDisplayed = NO;
271    [NSObject cancelPreviousPerformRequestsWithTarget:self
272                                             selector:@selector(fadeOutNotification:)
273                                               object:currentView];
274    
275    CGPoint fadeOutToPoint;
276    if (currentView.messagePosition == TSMessageNotificationPositionTop)
277    {
278        fadeOutToPoint = CGPointMake(currentView.center.x, -CGRectGetHeight(currentView.frame) / 2.0);;
279    }
280    else
281    {
282        fadeOutToPoint = CGPointMake(currentView.center.x,
283                                     currentView.viewController.view.bounds.size.height);
284    }
285    
286    [UIView animateWithDuration:kTSMessageAnimationDuration animations:^
287     {
288         currentView.center = fadeOutToPoint;
289         currentView.alpha = 0.0;
290     }
291                     completion:^(BOOL finished)
292     {
293         [currentView removeFromSuperview];
294         
295         if ([self.messages count] > 0)
296         {
297             [self.messages removeObjectAtIndex:0];
298         }
299         
300         notificationActive = NO;
301         
302         if ([self.messages count] > 0)
303         {
304             [self fadeInCurrentNotification];
305         }
306     }];
307}
308
309+ (BOOL)dismissActiveNotification
310{
311    if ([[TSMessage sharedMessage].messages count] == 0) return NO;
312    
313    dispatch_async(dispatch_get_main_queue(), ^
314    {
315        TSMessageView *currentMessage = [[TSMessage sharedMessage].messages objectAtIndex:0];
316        if (currentMessage.messageIsFullyDisplayed)
317        {
318            [[TSMessage sharedMessage] fadeOutNotification:currentMessage];
319        }
320    });
321    return YES;
322}
323
324#pragma mark class Methods to subclass
325
326+ (UIViewController *)defaultViewController
327{
328    NSLog(@"No view controller was set as parameter and TSMessage was not subclassed. If you want to subclass, implement defaultViewController to set the default viewController.");
329    return nil;
330    // Implement this in subclass
331}
332
333
334+ (CGFloat)navigationbarBottomOfViewController:(UIViewController *)viewController
335{
336    return 0;
337    // Implement this in subclass
338}
339
340@end