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