GIDTimedLoader.m 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. /*
  2. * Copyright 2023 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "GoogleSignIn/Sources/GIDTimedLoader/GIDTimedLoader.h"
  17. #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  18. @import UIKit;
  19. @import CoreMedia;
  20. #import "GoogleSignIn/Sources/GIDAppCheck/Implementations/GIDAppCheck.h"
  21. #import "GoogleSignIn/Sources/GIDAppCheck/UI/GIDActivityIndicatorViewController.h"
  22. CFTimeInterval const kGIDTimedLoaderMinAnimationDuration = 1.0;
  23. CFTimeInterval const kGIDTimedLoaderMaxDelayBeforeAnimating = 0.8;
  24. @interface GIDTimedLoader ()
  25. @property(nonatomic, strong) UIViewController *presentingViewController;
  26. @property(nonatomic, strong) GIDActivityIndicatorViewController *loadingViewController;
  27. @property(nonatomic, strong, nullable) NSTimer *loadingTimer;
  28. /// Timestamp representing when the loading view controller was presented and started animating
  29. @property(nonatomic) CFTimeInterval loadingTimeStamp;
  30. @end
  31. @implementation GIDTimedLoader
  32. - (instancetype)initWithPresentingViewController:(UIViewController *)presentingViewController {
  33. if (self = [super init]) {
  34. _presentingViewController = presentingViewController;
  35. _loadingViewController = [[GIDActivityIndicatorViewController alloc] init];
  36. _animationStatus = GIDTimedLoaderAnimationStatusNotStarted;
  37. }
  38. return self;
  39. }
  40. - (void)startTiming {
  41. if (self.animationStatus == GIDTimedLoaderAnimationStatusAnimating) {
  42. return;
  43. }
  44. self.animationStatus = GIDTimedLoaderAnimationStatusAnimating;
  45. self.loadingTimer = [NSTimer scheduledTimerWithTimeInterval:kGIDTimedLoaderMaxDelayBeforeAnimating
  46. target:self
  47. selector:@selector(presentLoadingViewController)
  48. userInfo:nil
  49. repeats:NO];
  50. }
  51. - (void)presentLoadingViewController {
  52. if (self.animationStatus == GIDTimedLoaderAnimationStatusStopped) {
  53. return;
  54. }
  55. self.animationStatus = GIDTimedLoaderAnimationStatusAnimating;
  56. self.loadingTimeStamp = CACurrentMediaTime();
  57. dispatch_async(dispatch_get_main_queue(), ^{
  58. // Since this loading VC may be reused, the activity indicator may have been stopped; restart it
  59. self.loadingViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
  60. self.loadingViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
  61. self.presentingViewController.definesPresentationContext = YES;
  62. [self.loadingViewController.activityIndicator startAnimating];
  63. [self.presentingViewController presentViewController:self.loadingViewController
  64. animated:YES
  65. completion:nil];
  66. });
  67. }
  68. - (void)stopTimingWithCompletion:(void (^)(void))completion {
  69. if (self.animationStatus != GIDTimedLoaderAnimationStatusAnimating) {
  70. return;
  71. }
  72. [self.loadingTimer invalidate];
  73. self.loadingTimer = nil;
  74. dispatch_time_t deadline = [self remainingDurationToAnimate];
  75. dispatch_after(deadline, dispatch_get_main_queue(), ^{
  76. self.animationStatus = GIDTimedLoaderAnimationStatusStopped;
  77. [self.loadingViewController.activityIndicator stopAnimating];
  78. [self.loadingViewController dismissViewControllerAnimated:YES completion:nil];
  79. completion();
  80. });
  81. }
  82. - (dispatch_time_t)remainingDurationToAnimate {
  83. // If we are not animating, then no need to wait
  84. if (self.animationStatus != GIDTimedLoaderAnimationStatusAnimating) {
  85. return 0;
  86. }
  87. CFTimeInterval now = CACurrentMediaTime();
  88. CFTimeInterval durationWaited = now - self.loadingTimeStamp;
  89. // If we have already waited for the minimum animation duration, then no need to wait
  90. if (durationWaited >= kGIDTimedLoaderMinAnimationDuration) {
  91. return 0;
  92. }
  93. CFTimeInterval diff = kGIDTimedLoaderMinAnimationDuration - durationWaited;
  94. int64_t diffNanos = diff * NSEC_PER_SEC;
  95. dispatch_time_t timeToWait = dispatch_time(DISPATCH_TIME_NOW, diffNanos);
  96. return timeToWait;
  97. }
  98. @end
  99. #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST