FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
Toast+UIView.m
1/***************************************************************************
2
3 Toast+UIView.h
4 Toast
5 Version 0.1
6
7 Copyright (c) 2011 Charles Scalesse.
8
9 Permission is hereby granted, free of charge, to any person obtaining a
10 copy of this software and associated documentation files (the
11 "Software"), to deal in the Software without restriction, including
12 without limitation the rights to use, copy, modify, merge, publish,
13 distribute, sublicense, and/or sell copies of the Software, and to
14 permit persons to whom the Software is furnished to do so, subject to
15 the following conditions:
16
17 The above copyright notice and this permission notice shall be included
18 in all copies or substantial portions of the Software.
19
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 ***************************************************************************/
29
30#import "Toast+UIView.h"
31#import <QuartzCore/QuartzCore.h>
32#import <objc/runtime.h>
33
34#define kMaxWidth 0.8
35#define kMaxHeight 0.8
36
37#define kHorizontalPadding 10.0
38#define kVerticalPadding 10.0
39#define kCornerRadius 10.0
40#define kOpacity 0.8
41#define kFontSize 16.0
42#define kMaxTitleLines 999
43#define kMaxMessageLines 999
44
45#define kFadeDuration 0.2
46
47#define kDefaultLength 3
48#define kDefaultPosition @"bottom"
49
50#define kImageWidth 80.0
51#define kImageHeight 80.0
52
53static NSString *kDurationKey = @"duration";
54
55@interface UIView (ToastPrivate)
56
57- (CGPoint)getPositionFor:(id)position toast:(UIView *)toast;
58- (UIView *)makeViewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image;
59
60@end
61
62@implementation UIView (Toast)
63
64#pragma mark -
65#pragma mark Toast Methods
66
67- (void)makeToast:(NSString *)message
68{
69 [self makeToast:message duration:kDefaultLength position:kDefaultPosition];
70}
71
72- (void)makeToast:(NSString *)message duration:(float)interval position:(id)point
73{
74 UIView *toast = [self makeViewForMessage:message title:nil image:nil];
75 [self showToast:toast duration:interval position:point];
76}
77
78- (void)makeToast:(NSString *)message
79 duration:(float)interval
80 position:(id)point
81 title:(NSString *)title
82{
83 UIView *toast = [self makeViewForMessage:message title:title image:nil];
84 [self showToast:toast duration:interval position:point];
85}
86
87- (void)makeToast:(NSString *)message
88 duration:(float)interval
89 position:(id)point
90 image:(UIImage *)image
91{
92 UIView *toast = [self makeViewForMessage:message title:nil image:image];
93 [self showToast:toast duration:interval position:point];
94}
95
96- (void)makeToast:(NSString *)message
97 duration:(float)interval
98 position:(id)point
99 title:(NSString *)title
100 image:(UIImage *)image
101{
102 UIView *toast = [self makeViewForMessage:message title:title image:image];
103 [self showToast:toast duration:interval position:point];
104}
105
106- (void)showToast:(UIView *)toast
107{
108 [self showToast:toast duration:kDefaultLength position:kDefaultPosition];
109}
110
111- (void)showToast:(UIView *)toast duration:(float)interval position:(id)point
112{
113
114 /****************************************************
115 * *
116 * Displays a view for a given duration & position. *
117 * *
118 ****************************************************/
119
120 CGPoint toastPoint = [self getPositionFor:point toast:toast];
121
122 // use an associative reference to associate the toast view with the display interval
123 objc_setAssociatedObject(toast, &kDurationKey, [NSNumber numberWithFloat:interval],
124 OBJC_ASSOCIATION_RETAIN);
125
126 [toast setCenter:toastPoint];
127 [toast setAlpha:0.0];
128 [self addSubview:toast];
129
130 [UIView beginAnimations:@"fade_in" context:toast];
131 [UIView setAnimationDuration:kFadeDuration];
132 [UIView setAnimationDelegate:self];
133 [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
134 [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
135 [toast setAlpha:1.0];
136 [UIView commitAnimations];
137}
138
139#pragma mark -
140#pragma mark Animation Delegate Method
141
142- (void)animationDidStop:(NSString *)animationID finished:(BOOL)finished context:(void *)context
143{
144
145 UIView *toast = (UIView *)context;
146
147 // retrieve the display interval associated with the view
148 float interval = [(NSNumber *)objc_getAssociatedObject(toast, &kDurationKey) floatValue];
149
150 if ([animationID isEqualToString:@"fade_in"])
151 {
152
153 [UIView beginAnimations:@"fade_out" context:toast];
154 [UIView setAnimationDelay:interval];
155 [UIView setAnimationDuration:kFadeDuration];
156 [UIView setAnimationDelegate:self];
157 [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
158 [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
159 [toast setAlpha:0.0];
160 [UIView commitAnimations];
161 }
162 else if ([animationID isEqualToString:@"fade_out"])
163 {
164
165 [toast removeFromSuperview];
166 }
167}
168
169#pragma mark -
170#pragma mark Private Methods
171
172- (CGPoint)getPositionFor:(id)point toast:(UIView *)toast
173{
174
175 /*************************************************************************************
176 * *
177 * Converts string literals @"top", @"bottom", @"center", or any point wrapped in an *
178 * NSValue object into a CGPoint *
179 * *
180 *************************************************************************************/
181
182 if ([point isKindOfClass:[NSString class]])
183 {
184
185 if ([point caseInsensitiveCompare:@"top"] == NSOrderedSame)
186 {
187 return CGPointMake(self.bounds.size.width / 2,
188 (toast.frame.size.height / 2) + kVerticalPadding);
189 }
190 else if ([point caseInsensitiveCompare:@"bottom"] == NSOrderedSame)
191 {
192 return CGPointMake(self.bounds.size.width / 2,
193 (self.bounds.size.height - (toast.frame.size.height / 2)) -
194 kVerticalPadding);
195 }
196 else if ([point caseInsensitiveCompare:@"center"] == NSOrderedSame)
197 {
198 return CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
199 }
200 }
201 else if ([point isKindOfClass:[NSValue class]])
202 {
203 return [point CGPointValue];
204 }
205
206 NSLog(@"Error: Invalid position for toast.");
207 return [self getPositionFor:kDefaultPosition toast:toast];
208}
209
210- (UIView *)makeViewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image
211{
212
213 /***********************************************************************************
214 * *
215 * Dynamically build a toast view with any combination of message, title, & image. *
216 * *
217 ***********************************************************************************/
218
219 if ((message == nil) && (title == nil) && (image == nil))
220 return nil;
221
222 UILabel *messageLabel = nil;
223 UILabel *titleLabel = nil;
224 UIImageView *imageView = nil;
225
226 // create the parent view
227 UIView *wrapperView = [[[UIView alloc] init] autorelease];
228 [wrapperView.layer setCornerRadius:kCornerRadius];
229 [wrapperView setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:kOpacity]];
230 wrapperView.autoresizingMask =
231 UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin |
232 UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
233
234 if (image != nil)
235 {
236 imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
237 [imageView setContentMode:UIViewContentModeScaleAspectFit];
238 [imageView
239 setFrame:CGRectMake(kHorizontalPadding, kVerticalPadding, kImageWidth, kImageHeight)];
240 }
241
242 float imageWidth, imageHeight, imageLeft;
243
244 // the imageView frame values will be used to size & position the other views
245 if (imageView != nil)
246 {
247 imageWidth = imageView.bounds.size.width;
248 imageHeight = imageView.bounds.size.height;
249 imageLeft = kHorizontalPadding;
250 }
251 else
252 {
253 imageWidth = imageHeight = imageLeft = 0;
254 }
255
256 if (title != nil)
257 {
258 titleLabel = [[[UILabel alloc] init] autorelease];
259 [titleLabel setNumberOfLines:kMaxTitleLines];
260 [titleLabel setFont:[UIFont boldSystemFontOfSize:kFontSize]];
261 [titleLabel setTextAlignment:UITextAlignmentLeft];
262 [titleLabel setLineBreakMode:UILineBreakModeWordWrap];
263 [titleLabel setTextColor:[UIColor whiteColor]];
264 [titleLabel setBackgroundColor:[UIColor clearColor]];
265 [titleLabel setAlpha:1.0];
266 [titleLabel setText:title];
267
268 // size the title label according to the length of the text
269 CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * kMaxWidth) - imageWidth,
270 self.bounds.size.height * kMaxHeight);
271 CGSize expectedSizeTitle = [title sizeWithFont:titleLabel.font
272 constrainedToSize:maxSizeTitle
273 lineBreakMode:titleLabel.lineBreakMode];
274 [titleLabel setFrame:CGRectMake(0, 0, expectedSizeTitle.width, expectedSizeTitle.height)];
275 }
276
277 if (message != nil)
278 {
279 messageLabel = [[[UILabel alloc] init] autorelease];
280 [messageLabel setNumberOfLines:kMaxMessageLines];
281 [messageLabel setFont:[UIFont systemFontOfSize:kFontSize]];
282 [messageLabel setLineBreakMode:UILineBreakModeWordWrap];
283 [messageLabel setTextColor:[UIColor whiteColor]];
284 [messageLabel setTextAlignment:UITextAlignmentCenter];
285 [messageLabel setBackgroundColor:[UIColor clearColor]];
286 [messageLabel setAlpha:1.0];
287 [messageLabel setText:message];
288
289 // size the message label according to the length of the text
290 CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * kMaxWidth) - imageWidth,
291 self.bounds.size.height * kMaxHeight);
292 CGSize expectedSizeMessage = [message sizeWithFont:messageLabel.font
293 constrainedToSize:maxSizeMessage
294 lineBreakMode:messageLabel.lineBreakMode];
295 [messageLabel
296 setFrame:CGRectMake(0, 0, expectedSizeMessage.width, expectedSizeMessage.height)];
297 }
298
299 // titleLabel frame values
300 float titleWidth, titleHeight, titleTop, titleLeft;
301
302 if (titleLabel != nil)
303 {
304 titleWidth = titleLabel.bounds.size.width;
305 titleHeight = titleLabel.bounds.size.height;
306 titleTop = kVerticalPadding;
307 titleLeft = imageLeft + imageWidth + kHorizontalPadding;
308 }
309 else
310 {
311 titleWidth = titleHeight = titleTop = titleLeft = 0;
312 }
313
314 // messageLabel frame values
315 float messageWidth, messageHeight, messageLeft, messageTop;
316
317 if (messageLabel != nil)
318 {
319 messageWidth = messageLabel.bounds.size.width;
320 messageHeight = messageLabel.bounds.size.height;
321 messageLeft = imageLeft + imageWidth + kHorizontalPadding;
322 messageTop = titleTop + titleHeight + kVerticalPadding;
323 }
324 else
325 {
326 messageWidth = messageHeight = messageLeft = messageTop = 0;
327 }
328
329 // compare the title & message widths and use the longer value to calculate the size of the
330 // wrapper width the same logic applies to the x value (left)
331 float longerWidth = (messageWidth < titleWidth) ? titleWidth : messageWidth;
332 float longerLeft = (messageLeft < titleLeft) ? titleLeft : messageLeft;
333
334 // if the image width is larger than longerWidth, use the image width to calculate the wrapper
335 // width. the same logic applies to the wrapper height
336 float wrapperWidth =
337 ((longerLeft + longerWidth + kHorizontalPadding) < imageWidth + (kHorizontalPadding * 2))
338 ? imageWidth + (kHorizontalPadding * 2)
339 : (longerLeft + longerWidth + kHorizontalPadding);
340 float wrapperHeight =
341 ((messageTop + messageHeight + kVerticalPadding) < imageHeight + (kVerticalPadding * 2))
342 ? imageHeight + (kVerticalPadding * 2)
343 : (messageTop + messageHeight + kVerticalPadding);
344
345 [wrapperView setFrame:CGRectMake(0, 0, wrapperWidth, wrapperHeight)];
346
347 if (titleLabel != nil)
348 {
349 [titleLabel setFrame:CGRectMake(titleLeft, titleTop, titleWidth, titleHeight)];
350 [wrapperView addSubview:titleLabel];
351 }
352
353 if (messageLabel != nil)
354 {
355 [messageLabel setFrame:CGRectMake(messageLeft, messageTop, messageWidth, messageHeight)];
356 [wrapperView addSubview:messageLabel];
357 }
358
359 if (imageView != nil)
360 {
361 [wrapperView addSubview:imageView];
362 }
363
364 return wrapperView;
365}
366
367@end