UIView+Toast.m 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. //
  2. // UIView+Toast.m
  3. // Toast
  4. //
  5. // Copyright (c) 2011-2016 Charles Scalesse.
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a
  8. // copy of this software and associated documentation files (the
  9. // "Software"), to deal in the Software without restriction, including
  10. // without limitation the rights to use, copy, modify, merge, publish,
  11. // distribute, sublicense, and/or sell copies of the Software, and to
  12. // permit persons to whom the Software is furnished to do so, subject to
  13. // the following conditions:
  14. //
  15. // The above copyright notice and this permission notice shall be included
  16. // in all copies or substantial portions of the Software.
  17. //
  18. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  19. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  20. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  21. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  22. // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  23. // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  24. // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  25. #import "UIView+Toast.h"
  26. #import <QuartzCore/QuartzCore.h>
  27. #import <objc/runtime.h>
  28. NSString * CSToastPositionTop = @"CSToastPositionTop";
  29. NSString * CSToastPositionCenter = @"CSToastPositionCenter";
  30. NSString * CSToastPositionBottom = @"CSToastPositionBottom";
  31. // Keys for values associated with toast views
  32. static const NSString * CSToastTimerKey = @"CSToastTimerKey";
  33. static const NSString * CSToastDurationKey = @"CSToastDurationKey";
  34. static const NSString * CSToastPositionKey = @"CSToastPositionKey";
  35. static const NSString * CSToastCompletionKey = @"CSToastCompletionKey";
  36. // Keys for values associated with self
  37. static const NSString * CSToastActiveKey = @"CSToastActiveKey";
  38. static const NSString * CSToastActivityViewKey = @"CSToastActivityViewKey";
  39. static const NSString * CSToastQueueKey = @"CSToastQueueKey";
  40. @interface UIView (ToastPrivate)
  41. /**
  42. These private methods are being prefixed with "cs_" to reduce the likelihood of non-obvious
  43. naming conflicts with other UIView methods.
  44. @discussion Should the public API also use the cs_ prefix? Technically it should, but it
  45. results in code that is less legible. The current public method names seem unlikely to cause
  46. conflicts so I think we should favor the cleaner API for now.
  47. */
  48. - (void)cs_showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position;
  49. - (void)cs_hideToast:(UIView *)toast;
  50. - (void)cs_hideToast:(UIView *)toast fromTap:(BOOL)fromTap;
  51. - (void)cs_toastTimerDidFinish:(NSTimer *)timer;
  52. - (void)cs_handleToastTapped:(UITapGestureRecognizer *)recognizer;
  53. - (CGPoint)cs_centerPointForPosition:(id)position withToast:(UIView *)toast;
  54. - (NSMutableArray *)cs_toastQueue;
  55. @end
  56. @implementation UIView (Toast)
  57. #pragma mark - Make Toast Methods
  58. - (void)makeToast:(NSString *)message {
  59. [self makeToast:message duration:[CSToastManager defaultDuration] position:[CSToastManager defaultPosition] style:nil];
  60. }
  61. - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position {
  62. [self makeToast:message duration:duration position:position style:nil];
  63. }
  64. - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position style:(CSToastStyle *)style {
  65. UIView *toast = [self toastViewForMessage:message title:nil image:nil style:style];
  66. [self showToast:toast duration:duration position:position completion:nil];
  67. }
  68. - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title image:(UIImage *)image style:(CSToastStyle *)style completion:(void(^)(BOOL didTap))completion {
  69. UIView *toast = [self toastViewForMessage:message title:title image:image style:style];
  70. [self showToast:toast duration:duration position:position completion:completion];
  71. }
  72. #pragma mark - Show Toast Methods
  73. - (void)showToast:(UIView *)toast {
  74. [self showToast:toast duration:[CSToastManager defaultDuration] position:[CSToastManager defaultPosition] completion:nil];
  75. }
  76. - (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position completion:(void(^)(BOOL didTap))completion {
  77. // sanity
  78. if (toast == nil) return;
  79. // store the completion block on the toast view
  80. objc_setAssociatedObject(toast, &CSToastCompletionKey, completion, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  81. if ([CSToastManager isQueueEnabled] && [self.cs_activeToasts count] > 0) {
  82. // we're about to queue this toast view so we need to store the duration and position as well
  83. objc_setAssociatedObject(toast, &CSToastDurationKey, @(duration), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  84. objc_setAssociatedObject(toast, &CSToastPositionKey, position, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  85. // enqueue
  86. [self.cs_toastQueue addObject:toast];
  87. } else {
  88. // present
  89. [self cs_showToast:toast duration:duration position:position];
  90. }
  91. }
  92. #pragma mark - Hide Toast Method
  93. - (void)hideToasts {
  94. for (UIView *toast in [self cs_activeToasts]) {
  95. [self hideToast:toast];
  96. }
  97. }
  98. - (void)hideToast:(UIView *)toast {
  99. // sanity
  100. if (!toast || ![[self cs_activeToasts] containsObject:toast]) return;
  101. NSTimer *timer = (NSTimer *)objc_getAssociatedObject(toast, &CSToastTimerKey);
  102. [timer invalidate];
  103. [self cs_hideToast:toast];
  104. }
  105. #pragma mark - Private Show/Hide Methods
  106. - (void)cs_showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position {
  107. toast.center = [self cs_centerPointForPosition:position withToast:toast];
  108. toast.alpha = 0.0;
  109. if ([CSToastManager isTapToDismissEnabled]) {
  110. UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cs_handleToastTapped:)];
  111. [toast addGestureRecognizer:recognizer];
  112. toast.userInteractionEnabled = YES;
  113. toast.exclusiveTouch = YES;
  114. }
  115. [[self cs_activeToasts] addObject:toast];
  116. [self addSubview:toast];
  117. [UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration]
  118. delay:0.0
  119. options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction)
  120. animations:^{
  121. toast.alpha = 1.0;
  122. } completion:^(BOOL finished) {
  123. NSTimer *timer = [NSTimer timerWithTimeInterval:duration target:self selector:@selector(cs_toastTimerDidFinish:) userInfo:toast repeats:NO];
  124. [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
  125. objc_setAssociatedObject(toast, &CSToastTimerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  126. }];
  127. }
  128. - (void)cs_hideToast:(UIView *)toast {
  129. [self cs_hideToast:toast fromTap:NO];
  130. }
  131. - (void)cs_hideToast:(UIView *)toast fromTap:(BOOL)fromTap {
  132. [UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration]
  133. delay:0.0
  134. options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
  135. animations:^{
  136. toast.alpha = 0.0;
  137. } completion:^(BOOL finished) {
  138. [toast removeFromSuperview];
  139. // remove
  140. [[self cs_activeToasts] removeObject:toast];
  141. // execute the completion block, if necessary
  142. void (^completion)(BOOL didTap) = objc_getAssociatedObject(toast, &CSToastCompletionKey);
  143. if (completion) {
  144. completion(fromTap);
  145. }
  146. if ([self.cs_toastQueue count] > 0) {
  147. // dequeue
  148. UIView *nextToast = [[self cs_toastQueue] firstObject];
  149. [[self cs_toastQueue] removeObjectAtIndex:0];
  150. // present the next toast
  151. NSTimeInterval duration = [objc_getAssociatedObject(nextToast, &CSToastDurationKey) doubleValue];
  152. id position = objc_getAssociatedObject(nextToast, &CSToastPositionKey);
  153. [self cs_showToast:nextToast duration:duration position:position];
  154. }
  155. }];
  156. }
  157. #pragma mark - View Construction
  158. - (UIView *)toastViewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image style:(CSToastStyle *)style {
  159. // sanity
  160. if(message == nil && title == nil && image == nil) return nil;
  161. // default to the shared style
  162. if (style == nil) {
  163. style = [CSToastManager sharedStyle];
  164. }
  165. // dynamically build a toast view with any combination of message, title, & image
  166. UILabel *messageLabel = nil;
  167. UILabel *titleLabel = nil;
  168. UIImageView *imageView = nil;
  169. UIView *wrapperView = [[UIView alloc] init];
  170. wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
  171. wrapperView.layer.cornerRadius = style.cornerRadius;
  172. if (style.displayShadow) {
  173. wrapperView.layer.shadowColor = style.shadowColor.CGColor;
  174. wrapperView.layer.shadowOpacity = style.shadowOpacity;
  175. wrapperView.layer.shadowRadius = style.shadowRadius;
  176. wrapperView.layer.shadowOffset = style.shadowOffset;
  177. }
  178. wrapperView.backgroundColor = style.backgroundColor;
  179. if(image != nil) {
  180. imageView = [[UIImageView alloc] initWithImage:image];
  181. imageView.contentMode = UIViewContentModeScaleAspectFit;
  182. imageView.frame = CGRectMake(style.horizontalPadding, style.verticalPadding, style.imageSize.width, style.imageSize.height);
  183. }
  184. CGRect imageRect = CGRectZero;
  185. if(imageView != nil) {
  186. imageRect.origin.x = style.horizontalPadding;
  187. imageRect.origin.y = style.verticalPadding;
  188. imageRect.size.width = imageView.bounds.size.width;
  189. imageRect.size.height = imageView.bounds.size.height;
  190. }
  191. if (title != nil) {
  192. titleLabel = [[UILabel alloc] init];
  193. titleLabel.numberOfLines = style.titleNumberOfLines;
  194. titleLabel.font = style.titleFont;
  195. titleLabel.textAlignment = style.titleAlignment;
  196. titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
  197. titleLabel.textColor = style.titleColor;
  198. titleLabel.backgroundColor = [UIColor clearColor];
  199. titleLabel.alpha = 1.0;
  200. titleLabel.text = title;
  201. // size the title label according to the length of the text
  202. CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, self.bounds.size.height * style.maxHeightPercentage);
  203. CGSize expectedSizeTitle = [titleLabel sizeThatFits:maxSizeTitle];
  204. // UILabel can return a size larger than the max size when the number of lines is 1
  205. expectedSizeTitle = CGSizeMake(MIN(maxSizeTitle.width, expectedSizeTitle.width), MIN(maxSizeTitle.height, expectedSizeTitle.height));
  206. titleLabel.frame = CGRectMake(0.0, 0.0, expectedSizeTitle.width, expectedSizeTitle.height);
  207. }
  208. if (message != nil) {
  209. messageLabel = [[UILabel alloc] init];
  210. messageLabel.numberOfLines = style.messageNumberOfLines;
  211. messageLabel.font = style.messageFont;
  212. messageLabel.textAlignment = style.messageAlignment;
  213. messageLabel.lineBreakMode = NSLineBreakByTruncatingTail;
  214. messageLabel.textColor = style.messageColor;
  215. messageLabel.backgroundColor = [UIColor clearColor];
  216. messageLabel.alpha = 1.0;
  217. messageLabel.text = message;
  218. CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, self.bounds.size.height * style.maxHeightPercentage);
  219. CGSize expectedSizeMessage = [messageLabel sizeThatFits:maxSizeMessage];
  220. // UILabel can return a size larger than the max size when the number of lines is 1
  221. expectedSizeMessage = CGSizeMake(MIN(maxSizeMessage.width, expectedSizeMessage.width), MIN(maxSizeMessage.height, expectedSizeMessage.height));
  222. messageLabel.frame = CGRectMake(0.0, 0.0, expectedSizeMessage.width, expectedSizeMessage.height);
  223. }
  224. CGRect titleRect = CGRectZero;
  225. if(titleLabel != nil) {
  226. titleRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding;
  227. titleRect.origin.y = style.verticalPadding;
  228. titleRect.size.width = titleLabel.bounds.size.width;
  229. titleRect.size.height = titleLabel.bounds.size.height;
  230. }
  231. CGRect messageRect = CGRectZero;
  232. if(messageLabel != nil) {
  233. messageRect.origin.x = imageRect.origin.x + imageRect.size.width + style.horizontalPadding;
  234. messageRect.origin.y = titleRect.origin.y + titleRect.size.height + style.verticalPadding;
  235. messageRect.size.width = messageLabel.bounds.size.width;
  236. messageRect.size.height = messageLabel.bounds.size.height;
  237. }
  238. CGFloat longerWidth = MAX(titleRect.size.width, messageRect.size.width);
  239. CGFloat longerX = MAX(titleRect.origin.x, messageRect.origin.x);
  240. // Wrapper width uses the longerWidth or the image width, whatever is larger. Same logic applies to the wrapper height.
  241. CGFloat wrapperWidth = MAX((imageRect.size.width + (style.horizontalPadding * 2.0)), (longerX + longerWidth + style.horizontalPadding));
  242. CGFloat wrapperHeight = MAX((messageRect.origin.y + messageRect.size.height + style.verticalPadding), (imageRect.size.height + (style.verticalPadding * 2.0)));
  243. wrapperView.frame = CGRectMake(0.0, 0.0, wrapperWidth, wrapperHeight);
  244. if(titleLabel != nil) {
  245. titleLabel.frame = titleRect;
  246. [wrapperView addSubview:titleLabel];
  247. }
  248. if(messageLabel != nil) {
  249. messageLabel.frame = messageRect;
  250. [wrapperView addSubview:messageLabel];
  251. }
  252. if(imageView != nil) {
  253. [wrapperView addSubview:imageView];
  254. }
  255. return wrapperView;
  256. }
  257. #pragma mark - Storage
  258. - (NSMutableArray *)cs_activeToasts {
  259. NSMutableArray *cs_activeToasts = objc_getAssociatedObject(self, &CSToastActiveKey);
  260. if (cs_activeToasts == nil) {
  261. cs_activeToasts = [[NSMutableArray alloc] init];
  262. objc_setAssociatedObject(self, &CSToastActiveKey, cs_activeToasts, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  263. }
  264. return cs_activeToasts;
  265. }
  266. - (NSMutableArray *)cs_toastQueue {
  267. NSMutableArray *cs_toastQueue = objc_getAssociatedObject(self, &CSToastQueueKey);
  268. if (cs_toastQueue == nil) {
  269. cs_toastQueue = [[NSMutableArray alloc] init];
  270. objc_setAssociatedObject(self, &CSToastQueueKey, cs_toastQueue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  271. }
  272. return cs_toastQueue;
  273. }
  274. #pragma mark - Events
  275. - (void)cs_toastTimerDidFinish:(NSTimer *)timer {
  276. [self cs_hideToast:(UIView *)timer.userInfo];
  277. }
  278. - (void)cs_handleToastTapped:(UITapGestureRecognizer *)recognizer {
  279. UIView *toast = recognizer.view;
  280. NSTimer *timer = (NSTimer *)objc_getAssociatedObject(toast, &CSToastTimerKey);
  281. [timer invalidate];
  282. [self cs_hideToast:toast fromTap:YES];
  283. }
  284. #pragma mark - Activity Methods
  285. - (void)makeToastActivity:(id)position {
  286. // sanity
  287. UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey);
  288. if (existingActivityView != nil) return;
  289. CSToastStyle *style = [CSToastManager sharedStyle];
  290. UIView *activityView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, style.activitySize.width, style.activitySize.height)];
  291. activityView.center = [self cs_centerPointForPosition:position withToast:activityView];
  292. activityView.backgroundColor = style.backgroundColor;
  293. activityView.alpha = 0.0;
  294. activityView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin);
  295. activityView.layer.cornerRadius = style.cornerRadius;
  296. if (style.displayShadow) {
  297. activityView.layer.shadowColor = style.shadowColor.CGColor;
  298. activityView.layer.shadowOpacity = style.shadowOpacity;
  299. activityView.layer.shadowRadius = style.shadowRadius;
  300. activityView.layer.shadowOffset = style.shadowOffset;
  301. }
  302. UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
  303. activityIndicatorView.center = CGPointMake(activityView.bounds.size.width / 2, activityView.bounds.size.height / 2);
  304. [activityView addSubview:activityIndicatorView];
  305. [activityIndicatorView startAnimating];
  306. // associate the activity view with self
  307. objc_setAssociatedObject (self, &CSToastActivityViewKey, activityView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  308. [self addSubview:activityView];
  309. [UIView animateWithDuration:style.fadeDuration
  310. delay:0.0
  311. options:UIViewAnimationOptionCurveEaseOut
  312. animations:^{
  313. activityView.alpha = 1.0;
  314. } completion:nil];
  315. }
  316. - (void)hideToastActivity {
  317. UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey);
  318. if (existingActivityView != nil) {
  319. [UIView animateWithDuration:[[CSToastManager sharedStyle] fadeDuration]
  320. delay:0.0
  321. options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
  322. animations:^{
  323. existingActivityView.alpha = 0.0;
  324. } completion:^(BOOL finished) {
  325. [existingActivityView removeFromSuperview];
  326. objc_setAssociatedObject (self, &CSToastActivityViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  327. }];
  328. }
  329. }
  330. #pragma mark - Helpers
  331. - (CGPoint)cs_centerPointForPosition:(id)point withToast:(UIView *)toast {
  332. CSToastStyle *style = [CSToastManager sharedStyle];
  333. if([point isKindOfClass:[NSString class]]) {
  334. if([point caseInsensitiveCompare:CSToastPositionTop] == NSOrderedSame) {
  335. return CGPointMake(self.bounds.size.width/2, (toast.frame.size.height / 2) + style.verticalPadding);
  336. } else if([point caseInsensitiveCompare:CSToastPositionCenter] == NSOrderedSame) {
  337. return CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
  338. }
  339. } else if ([point isKindOfClass:[NSValue class]]) {
  340. return [point CGPointValue];
  341. }
  342. // default to bottom
  343. return CGPointMake(self.bounds.size.width/2, (self.bounds.size.height - (toast.frame.size.height / 2)) - style.verticalPadding);
  344. }
  345. @end
  346. @implementation CSToastStyle
  347. #pragma mark - Constructors
  348. - (instancetype)initWithDefaultStyle {
  349. self = [super init];
  350. if (self) {
  351. self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.8];
  352. self.titleColor = [UIColor whiteColor];
  353. self.messageColor = [UIColor whiteColor];
  354. self.maxWidthPercentage = 0.8;
  355. self.maxHeightPercentage = 0.8;
  356. self.horizontalPadding = 10.0;
  357. self.verticalPadding = 10.0;
  358. self.cornerRadius = 10.0;
  359. self.titleFont = [UIFont boldSystemFontOfSize:16.0];
  360. self.messageFont = [UIFont systemFontOfSize:16.0];
  361. self.titleAlignment = NSTextAlignmentLeft;
  362. self.messageAlignment = NSTextAlignmentLeft;
  363. self.titleNumberOfLines = 0;
  364. self.messageNumberOfLines = 0;
  365. self.displayShadow = NO;
  366. self.shadowOpacity = 0.8;
  367. self.shadowRadius = 6.0;
  368. self.shadowOffset = CGSizeMake(4.0, 4.0);
  369. self.imageSize = CGSizeMake(80.0, 80.0);
  370. self.activitySize = CGSizeMake(100.0, 100.0);
  371. self.fadeDuration = 0.2;
  372. }
  373. return self;
  374. }
  375. - (void)setMaxWidthPercentage:(CGFloat)maxWidthPercentage {
  376. _maxWidthPercentage = MAX(MIN(maxWidthPercentage, 1.0), 0.0);
  377. }
  378. - (void)setMaxHeightPercentage:(CGFloat)maxHeightPercentage {
  379. _maxHeightPercentage = MAX(MIN(maxHeightPercentage, 1.0), 0.0);
  380. }
  381. - (instancetype)init NS_UNAVAILABLE {
  382. return nil;
  383. }
  384. @end
  385. @interface CSToastManager ()
  386. @property (strong, nonatomic) CSToastStyle *sharedStyle;
  387. @property (assign, nonatomic, getter=isTapToDismissEnabled) BOOL tapToDismissEnabled;
  388. @property (assign, nonatomic, getter=isQueueEnabled) BOOL queueEnabled;
  389. @property (assign, nonatomic) NSTimeInterval defaultDuration;
  390. @property (strong, nonatomic) id defaultPosition;
  391. @end
  392. @implementation CSToastManager
  393. #pragma mark - Constructors
  394. + (instancetype)sharedManager {
  395. static CSToastManager *_sharedManager = nil;
  396. static dispatch_once_t oncePredicate;
  397. dispatch_once(&oncePredicate, ^{
  398. _sharedManager = [[self alloc] init];
  399. });
  400. return _sharedManager;
  401. }
  402. - (instancetype)init {
  403. self = [super init];
  404. if (self) {
  405. self.sharedStyle = [[CSToastStyle alloc] initWithDefaultStyle];
  406. self.tapToDismissEnabled = YES;
  407. self.queueEnabled = YES;
  408. self.defaultDuration = 3.0;
  409. self.defaultPosition = CSToastPositionBottom;
  410. }
  411. return self;
  412. }
  413. #pragma mark - Singleton Methods
  414. + (void)setSharedStyle:(CSToastStyle *)sharedStyle {
  415. [[self sharedManager] setSharedStyle:sharedStyle];
  416. }
  417. + (CSToastStyle *)sharedStyle {
  418. return [[self sharedManager] sharedStyle];
  419. }
  420. + (void)setTapToDismissEnabled:(BOOL)tapToDismissEnabled {
  421. [[self sharedManager] setTapToDismissEnabled:tapToDismissEnabled];
  422. }
  423. + (BOOL)isTapToDismissEnabled {
  424. return [[self sharedManager] isTapToDismissEnabled];
  425. }
  426. + (void)setQueueEnabled:(BOOL)queueEnabled {
  427. [[self sharedManager] setQueueEnabled:queueEnabled];
  428. }
  429. + (BOOL)isQueueEnabled {
  430. return [[self sharedManager] isQueueEnabled];
  431. }
  432. + (void)setDefaultDuration:(NSTimeInterval)duration {
  433. [[self sharedManager] setDefaultDuration:duration];
  434. }
  435. + (NSTimeInterval)defaultDuration {
  436. return [[self sharedManager] defaultDuration];
  437. }
  438. + (void)setDefaultPosition:(id)position {
  439. if ([position isKindOfClass:[NSString class]] || [position isKindOfClass:[NSValue class]]) {
  440. [[self sharedManager] setDefaultPosition:position];
  441. }
  442. }
  443. + (id)defaultPosition {
  444. return [[self sharedManager] defaultPosition];
  445. }
  446. @end