react-native-navigation的迁移库

RCCNotification.m 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. #import "RCCNotification.h"
  2. #import <React/RCTRootView.h>
  3. #import "RCTHelpers.h"
  4. @interface NotificationView : UIView
  5. @property (nonatomic, strong) RCTRootView *reactView;
  6. @property (nonatomic, strong) UIView *slideAnimGapView;
  7. @property (nonatomic, strong) NSDictionary *params;
  8. @property (nonatomic, strong) NSTimer *autoDismissTimer;
  9. @property (nonatomic) BOOL yellowBoxRemoved;
  10. @end
  11. @implementation NotificationView
  12. -(id)initWithParams:(NSDictionary*)params
  13. {
  14. self = [super initWithFrame:CGRectZero];
  15. if (self)
  16. {
  17. self.params = params;
  18. self.yellowBoxRemoved = NO;
  19. self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  20. self.reactView = [[RCTRootView alloc] initWithBridge:[[RCCManager sharedInstance] getBridge] moduleName:params[@"component"] initialProperties:params[@"passProps"]];
  21. self.reactView.backgroundColor = [UIColor clearColor];
  22. self.reactView.sizeFlexibility = RCTRootViewSizeFlexibilityWidthAndHeight;
  23. self.reactView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  24. [self addSubview:self.reactView];
  25. [self.reactView.contentView.layer addObserver:self forKeyPath:@"frame" options:0 context:nil];
  26. [self.reactView.contentView.layer addObserver:self forKeyPath:@"bounds" options:0 context:NULL];
  27. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onRNReload) name:RCTJavaScriptWillStartLoadingNotification object:nil];
  28. if ([params[@"dismissWithSwipe"] boolValue])
  29. {
  30. UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(performDismiss)];
  31. swipeGesture.direction = [self swipeDirection];
  32. [self addGestureRecognizer:swipeGesture];
  33. }
  34. if (params[@"shadowRadius"] != nil && [params[@"shadowRadius"] floatValue] > 0)
  35. {
  36. self.layer.shadowColor = [UIColor blackColor].CGColor;
  37. self.layer.shadowOffset = CGSizeMake(0, 0);
  38. self.layer.shadowRadius = [params[@"shadowRadius"] floatValue];
  39. self.layer.shadowOpacity = 0.6;
  40. }
  41. self.hidden = YES;
  42. }
  43. return self;
  44. }
  45. -(void)layoutSubviews
  46. {
  47. [super layoutSubviews];
  48. if(!self.yellowBoxRemoved)
  49. {
  50. self.yellowBoxRemoved = [RCTHelpers removeYellowBox:self.reactView];
  51. }
  52. }
  53. -(UISwipeGestureRecognizerDirection)swipeDirection
  54. {
  55. UISwipeGestureRecognizerDirection direction = UISwipeGestureRecognizerDirectionUp;
  56. NSString *animationType = [self.params valueForKeyPath:@"animation.type"];
  57. if ([animationType isEqualToString:@"swing"] || [animationType isEqualToString:@"slide-down"])
  58. direction = UISwipeGestureRecognizerDirectionUp;
  59. else if ([animationType isEqualToString:@"slide-left"])
  60. direction = UISwipeGestureRecognizerDirectionRight;
  61. else if ([animationType isEqualToString:@"slide-right"])
  62. direction = UISwipeGestureRecognizerDirectionLeft;
  63. return direction;
  64. }
  65. -(void)killAutoDismissTimer
  66. {
  67. if (self.autoDismissTimer != nil)
  68. {
  69. [self.autoDismissTimer invalidate];
  70. self.autoDismissTimer = nil;
  71. }
  72. }
  73. -(void)removeObservers
  74. {
  75. [[NSNotificationCenter defaultCenter] removeObserver:self];
  76. [self.reactView.contentView.layer removeObserver:self forKeyPath:@"frame" context:nil];
  77. [self.reactView.contentView.layer removeObserver:self forKeyPath:@"bounds" context:NULL];
  78. }
  79. -(void)cleanup
  80. {
  81. [self removeObservers];
  82. [self killAutoDismissTimer];
  83. }
  84. -(void)dealloc
  85. {
  86. [self cleanup];
  87. }
  88. -(void)onRNReload
  89. {
  90. [self cleanup];
  91. [self removeFromSuperview];
  92. self.reactView = nil;
  93. }
  94. -(UIColor*)reactViewAvgColor
  95. {
  96. if (self.reactView.contentView.frame.size.width == 0 || self.reactView.contentView.frame.size.height == 0)
  97. {
  98. return [UIColor clearColor];
  99. }
  100. UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0);
  101. [self.reactView.contentView drawViewHierarchyInRect:CGRectMake(0, 0, 1, 1) afterScreenUpdates:YES];
  102. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  103. UIGraphicsEndImageContext();
  104. CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
  105. const UInt8* data = CFDataGetBytePtr(pixelData);
  106. CFRelease(pixelData);
  107. //after scale defaults to bgr
  108. CGFloat red = data[2] / 255.0f,
  109. green = data[1] / 255.0f,
  110. blue = data[0] / 255.0f,
  111. alpha = data[3] / 255.0f;
  112. UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
  113. return color;
  114. }
  115. -(BOOL)shouldAddSlidingAnimGapView
  116. {
  117. if (![[self.params valueForKeyPath:@"animation.animated"] boolValue])
  118. {
  119. return NO;
  120. }
  121. NSString *animationType = [self.params valueForKeyPath:@"animation.type"];
  122. if (![animationType isEqualToString:@"slide-down"])
  123. {
  124. return NO;
  125. }
  126. if (![self.params valueForKeyPath:@"animation.damping"])
  127. {
  128. return NO;
  129. }
  130. CGFloat damping = [[self.params valueForKeyPath:@"animation.damping"] floatValue];
  131. if (damping >= 1)
  132. {
  133. return NO;
  134. }
  135. return YES;
  136. }
  137. -(BOOL)isBottomPosition
  138. {
  139. return ((self.params[@"position"] != nil) && ([self.params[@"position"] isEqualToString:@"bottom"]));
  140. }
  141. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  142. {
  143. CGSize frameSize = CGSizeZero;
  144. if ([object isKindOfClass:[CALayer class]])
  145. frameSize = ((CALayer*)object).frame.size;
  146. if ([object isKindOfClass:[UIView class]])
  147. frameSize = ((UIView*)object).frame.size;
  148. if (!CGSizeEqualToSize(frameSize, self.reactView.frame.size))
  149. {
  150. BOOL isBottomPosition = [self isBottomPosition];
  151. CGFloat yPos = isBottomPosition ? self.superview.bounds.size.height - frameSize.height : 0;
  152. self.frame = CGRectMake((self.superview.frame.size.width - frameSize.width) / 2.0, yPos, frameSize.width, frameSize.height);
  153. self.reactView.frame = CGRectMake(0, 0, frameSize.width, frameSize.height);
  154. self.reactView.contentView.frame = CGRectMake(0, 0, frameSize.width, frameSize.height);
  155. //if necessary, add a view with the same color to cover the gap if there's a slide animation with spring
  156. if ([self shouldAddSlidingAnimGapView])
  157. {
  158. if (self.slideAnimGapView == nil)
  159. {
  160. self.slideAnimGapView = [[UIView alloc] initWithFrame:CGRectZero];
  161. [self.reactView addSubview:self.slideAnimGapView];
  162. }
  163. CGFloat yPos = isBottomPosition ? frameSize.height : -20;
  164. self.slideAnimGapView.frame = CGRectMake(0, yPos, self.superview.frame.size.width, 20);
  165. dispatch_async(dispatch_get_main_queue(), ^
  166. {
  167. self.slideAnimGapView.backgroundColor = [self reactViewAvgColor];
  168. });
  169. }
  170. if (self.params[@"shadowRadius"] != nil && [self.params[@"shadowRadius"] floatValue] > 0)
  171. {
  172. self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
  173. }
  174. }
  175. }
  176. -(void)performDismiss
  177. {
  178. [RCCNotification dismissWithParams:self.params resolver:nil rejecter:nil];
  179. }
  180. -(void)startAutoDismissTimerIfNecessary
  181. {
  182. if (self.params[@"autoDismissTimerSec"])
  183. {
  184. [self killAutoDismissTimer];
  185. CGFloat interval = [self.params[@"autoDismissTimerSec"] floatValue];
  186. interval = MAX(interval, 1);
  187. self.autoDismissTimer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(performDismiss) userInfo:nil repeats:NO];
  188. }
  189. }
  190. -(void)applyAnimations
  191. {
  192. NSString *animationType = [self.params valueForKeyPath:@"animation.type"];
  193. if ([animationType isEqualToString:@"swing"])
  194. {
  195. CATransform3D transform = CATransform3DIdentity;
  196. transform.m34 = -0.002f;
  197. transform.m24 = -0.007f;
  198. if (self.layer.anchorPoint.x == 0.5 && self.layer.anchorPoint.y == 0.5)
  199. {
  200. CGPoint anchorPoint = CGPointMake(0.5, 0);
  201. CGFloat yOffset = -self.layer.frame.size.height / 2.0;
  202. if([self isBottomPosition])
  203. {
  204. anchorPoint = CGPointMake(0.5, 1);
  205. yOffset = self.layer.frame.size.height / 2.0;
  206. }
  207. self.layer.anchorPoint = anchorPoint;
  208. self.layer.frame = CGRectOffset(self.layer.frame, 0, yOffset);
  209. }
  210. self.layer.zPosition = 1000;
  211. self.layer.transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
  212. }
  213. else
  214. {
  215. int slideAnimationSign = [self isBottomPosition] ? 1 : -1;
  216. CGAffineTransform transform = CGAffineTransformIdentity;
  217. if ([animationType isEqualToString:@"slide-down"])
  218. transform = CGAffineTransformMakeTranslation(0, slideAnimationSign * self.frame.size.height);
  219. else if ([animationType isEqualToString:@"slide-left"])
  220. transform = CGAffineTransformMakeTranslation(self.frame.size.width, 0);
  221. else if ([animationType isEqualToString:@"slide-right"])
  222. transform = CGAffineTransformMakeTranslation(-self.frame.size.width, 0);
  223. self.transform = transform;
  224. }
  225. if ( [[self.params valueForKeyPath:@"animation.fade"] boolValue])
  226. {
  227. self.alpha = 0;
  228. }
  229. }
  230. #pragma mark - show/dismiss
  231. -(void)showAnimationEnded:(void (^)(void))completion
  232. {
  233. self.alpha = 1;
  234. if (completion)
  235. {
  236. completion();
  237. }
  238. [self startAutoDismissTimerIfNecessary];
  239. }
  240. -(void)dismissAnimationEnded:(void (^)(void))completion
  241. {
  242. if (completion)
  243. {
  244. completion();
  245. }
  246. [self removeFromSuperview];
  247. }
  248. -(void)performShowWithCompletion:(void (^)(void))completion
  249. {
  250. self.hidden = NO;
  251. if ([[self.params valueForKeyPath:@"animation.animated"] boolValue])
  252. {
  253. CGFloat duration = [[self.params valueForKeyPath:@"animation.duration"] floatValue];
  254. CGFloat delay = [[self.params valueForKeyPath:@"animation.delay"] floatValue];
  255. CGFloat damping = 1;
  256. if ([self.params valueForKeyPath:@"animation.damping"] != nil)
  257. {
  258. damping = [[self.params valueForKeyPath:@"animation.damping"] floatValue];
  259. damping = MAX(damping, 0);
  260. damping = MIN(damping, 1);
  261. }
  262. [self applyAnimations];
  263. [UIView animateWithDuration:duration delay:delay usingSpringWithDamping:damping initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseOut animations:^()
  264. {
  265. self.alpha = 1;
  266. self.transform = CGAffineTransformIdentity;
  267. self.layer.transform = CATransform3DIdentity;
  268. }
  269. completion:^(BOOL finished)
  270. {
  271. [self showAnimationEnded:completion];
  272. }];
  273. }
  274. else
  275. {
  276. [self showAnimationEnded:completion];
  277. }
  278. }
  279. -(void)showWithCompletion:(void (^)(void))completion
  280. {
  281. if (self.frame.size.height == 0 || self.frame.size.width == 0)
  282. {
  283. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)),
  284. dispatch_get_main_queue(),
  285. ^{
  286. [self showWithCompletion:completion];
  287. });
  288. }
  289. else
  290. {
  291. [self performShowWithCompletion:completion];
  292. }
  293. }
  294. -(void)dismissWithCompletion:(void (^)(void))completion
  295. {
  296. [self killAutoDismissTimer];
  297. [self.reactView cancelTouches];
  298. if ([[self.params valueForKeyPath:@"animation.animated"] boolValue])
  299. {
  300. CGFloat duration = [[self.params valueForKeyPath:@"animation.duration"] floatValue] * 0.75;
  301. [UIView animateWithDuration:duration delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseIn
  302. animations:^()
  303. {
  304. [self applyAnimations];
  305. }
  306. completion:^(BOOL finished)
  307. {
  308. [self dismissAnimationEnded:completion];
  309. }];
  310. }
  311. else
  312. {
  313. [self dismissAnimationEnded:completion];
  314. }
  315. }
  316. @end
  317. @interface PendingNotification : NSObject
  318. @property (nonatomic, copy) void (^resolve)(id result);
  319. @property (nonatomic, copy) void (^reject)(NSString *code, NSString *message, NSError *error);
  320. @property (nonatomic, strong) NSDictionary *params;
  321. @end
  322. @implementation PendingNotification
  323. @end
  324. @implementation RCCNotification
  325. static NSTimeInterval gLastShownTime = NSTimeIntervalSince1970;
  326. static NSMutableArray *gPendingNotifications = nil;
  327. static NSMutableArray *gShownNotificationViews = nil;
  328. +(void)showWithParams:(NSDictionary*)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
  329. {
  330. if(gPendingNotifications == nil)
  331. gPendingNotifications = [NSMutableArray array];
  332. if(gShownNotificationViews == nil)
  333. gShownNotificationViews = [NSMutableArray array];
  334. if ([gShownNotificationViews count] > 0)
  335. {
  336. for (NotificationView *notificationView in gShownNotificationViews)
  337. {
  338. [notificationView killAutoDismissTimer];
  339. }
  340. //limit the amount of consecutive notifications per second. If they arrive too fast, the last one will be remembered as pending
  341. if(CFAbsoluteTimeGetCurrent() - gLastShownTime < 1)
  342. {
  343. PendingNotification *pendingNotification = [PendingNotification new];
  344. pendingNotification.params = params;
  345. pendingNotification.resolve = resolve;
  346. pendingNotification.reject = reject;
  347. [gPendingNotifications addObject:pendingNotification];
  348. return;
  349. }
  350. }
  351. gLastShownTime = CFAbsoluteTimeGetCurrent();
  352. UIWindow *window = [[RCCManager sharedInstance] getAppWindow];
  353. NotificationView *notificationView = [[NotificationView alloc] initWithParams:params];
  354. [window addSubview:notificationView];
  355. [gShownNotificationViews addObject:notificationView];
  356. [notificationView showWithCompletion:^()
  357. {
  358. if (resolve)
  359. {
  360. resolve(nil);
  361. }
  362. }];
  363. }
  364. +(void)dismissWithParams:(NSDictionary*)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
  365. {
  366. int count = [gShownNotificationViews count];
  367. for (int i = count - 1 ; i >= 0; i--)
  368. {
  369. NotificationView *notificationView = [gShownNotificationViews objectAtIndex:i];
  370. [gShownNotificationViews removeObject:notificationView];
  371. [notificationView dismissWithCompletion:^()
  372. {
  373. if (i == (count - 1))
  374. {
  375. if(resolve)
  376. {
  377. resolve(nil);
  378. }
  379. if ([gPendingNotifications count] > 0)
  380. {
  381. PendingNotification *pendingNotification = [gPendingNotifications lastObject];
  382. [self showWithParams:pendingNotification.params resolver:pendingNotification.resolve rejecter:pendingNotification.reject];
  383. [gPendingNotifications removeLastObject];
  384. }
  385. }
  386. }];
  387. }
  388. }
  389. @end