2017-05-15 12:27:07 -07:00
|
|
|
/*
|
2019-04-23 12:08:39 -07:00
|
|
|
* Copyright 2019 Google
|
2017-05-15 12:27:07 -07:00
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#import "UIViewController+Alerts.h"
|
|
|
|
|
|
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
|
|
|
|
|
|
/*! @var kPleaseWaitAssociatedObjectKey
|
|
|
|
|
@brief Key used to identify the "please wait" spinner associated object.
|
|
|
|
|
*/
|
|
|
|
|
static NSString *const kPleaseWaitAssociatedObjectKey =
|
|
|
|
|
@"_UIViewControllerAlertCategory_PleaseWaitScreenAssociatedObject";
|
|
|
|
|
|
|
|
|
|
/*! @var kUseStatusBarSpinnerAssociatedObjectKey
|
|
|
|
|
@brief The address of this constant is the key used to identify the "use status bar spinner"
|
|
|
|
|
associated object.
|
|
|
|
|
*/
|
|
|
|
|
static const void *const kUseStatusBarSpinnerAssociatedObjectKey;
|
|
|
|
|
|
|
|
|
|
/*! @var kOK
|
|
|
|
|
@brief Text for an 'OK' button.
|
|
|
|
|
*/
|
|
|
|
|
static NSString *const kOK = @"OK";
|
|
|
|
|
|
|
|
|
|
/*! @var kCancel
|
|
|
|
|
@brief Text for an 'Cancel' button.
|
|
|
|
|
*/
|
|
|
|
|
static NSString *const kCancel = @"Cancel";
|
|
|
|
|
|
|
|
|
|
/*! @class SimpleTextPromptDelegate
|
|
|
|
|
@brief A @c UIAlertViewDelegate which allows @c UIAlertView to be used with blocks more easily.
|
|
|
|
|
*/
|
|
|
|
|
@interface SimpleTextPromptDelegate : NSObject <UIAlertViewDelegate>
|
|
|
|
|
|
|
|
|
|
/*! @fn init
|
|
|
|
|
@brief Please use initWithCompletionHandler.
|
|
|
|
|
*/
|
|
|
|
|
- (nullable instancetype)init NS_UNAVAILABLE;
|
|
|
|
|
|
|
|
|
|
/*! @fn initWithCompletionHandler:
|
|
|
|
|
@brief Designated initializer.
|
|
|
|
|
@param completionHandler The block to call when the alert view is dismissed.
|
|
|
|
|
*/
|
|
|
|
|
- (nullable instancetype)initWithCompletionHandler:(AlertPromptCompletionBlock)completionHandler
|
|
|
|
|
NS_DESIGNATED_INITIALIZER;
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation UIViewController (Alerts)
|
|
|
|
|
|
|
|
|
|
- (void)setUseStatusBarSpinner:(BOOL)useStatusBarSpinner {
|
|
|
|
|
objc_setAssociatedObject(self,
|
|
|
|
|
&kUseStatusBarSpinnerAssociatedObjectKey,
|
|
|
|
|
useStatusBarSpinner ? @(YES) : nil,
|
|
|
|
|
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)useStatusBarSpinner {
|
|
|
|
|
return objc_getAssociatedObject(self, &kUseStatusBarSpinnerAssociatedObjectKey) ? YES : NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! @fn supportsAlertController
|
|
|
|
|
@brief Determines if the current platform supports @c UIAlertController.
|
|
|
|
|
@return YES if the current platform supports @c UIAlertController.
|
|
|
|
|
*/
|
|
|
|
|
- (BOOL)supportsAlertController {
|
|
|
|
|
return NSClassFromString(@"UIAlertController") != nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showMessagePrompt:(NSString *)message {
|
|
|
|
|
[self showMessagePromptWithTitle:nil message:message showCancelButton:NO completion:nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showMessagePromptWithTitle:(nullable NSString *)title
|
|
|
|
|
message:(NSString *)message
|
|
|
|
|
showCancelButton:(BOOL)showCancelButton
|
|
|
|
|
completion:(nullable AlertPromptCompletionBlock)completion {
|
|
|
|
|
if (message) {
|
|
|
|
|
[UIPasteboard generalPasteboard].string = message;
|
|
|
|
|
}
|
|
|
|
|
if ([self supportsAlertController]) {
|
|
|
|
|
UIAlertController *alert =
|
|
|
|
|
[UIAlertController alertControllerWithTitle:title
|
|
|
|
|
message:message
|
|
|
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
|
UIAlertAction *okAction =
|
|
|
|
|
[UIAlertAction actionWithTitle:kOK
|
|
|
|
|
style:UIAlertActionStyleDefault
|
|
|
|
|
handler:^(UIAlertAction * _Nonnull action) {
|
|
|
|
|
if (completion) {
|
|
|
|
|
completion(YES, nil);
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
[alert addAction:okAction];
|
|
|
|
|
|
|
|
|
|
if (showCancelButton) {
|
|
|
|
|
UIAlertAction *cancelAction =
|
|
|
|
|
[UIAlertAction actionWithTitle:kCancel
|
|
|
|
|
style:UIAlertActionStyleCancel
|
|
|
|
|
handler:^(UIAlertAction * _Nonnull action) {
|
|
|
|
|
completion(NO, nil);
|
|
|
|
|
}];
|
|
|
|
|
[alert addAction:cancelAction];
|
|
|
|
|
}
|
|
|
|
|
[self presentViewController:alert animated:YES completion:nil];
|
|
|
|
|
} else {
|
2019-04-23 12:08:39 -07:00
|
|
|
UIAlertController* alert = [UIAlertController alertControllerWithTitle:title
|
|
|
|
|
message:message
|
|
|
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
|
[self presentViewController:alert animated:YES completion:nil];
|
2017-05-15 12:27:07 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showTextInputPromptWithMessage:(NSString *)message
|
|
|
|
|
completionBlock:(AlertPromptCompletionBlock)completion {
|
|
|
|
|
[self showTextInputPromptWithMessage:message
|
|
|
|
|
keyboardType:UIKeyboardTypeDefault
|
|
|
|
|
completionBlock:completion];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showTextInputPromptWithMessage:(NSString *)message
|
|
|
|
|
keyboardType:(UIKeyboardType)keyboardType
|
|
|
|
|
completionBlock:(nonnull AlertPromptCompletionBlock)completion {
|
|
|
|
|
if ([self supportsAlertController]) {
|
|
|
|
|
UIAlertController *prompt =
|
|
|
|
|
[UIAlertController alertControllerWithTitle:nil
|
|
|
|
|
message:message
|
|
|
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
|
__weak UIAlertController *weakPrompt = prompt;
|
|
|
|
|
UIAlertAction *cancelAction =
|
|
|
|
|
[UIAlertAction actionWithTitle:kCancel
|
|
|
|
|
style:UIAlertActionStyleCancel
|
|
|
|
|
handler:^(UIAlertAction * _Nonnull action) {
|
|
|
|
|
completion(NO, nil);
|
|
|
|
|
}];
|
|
|
|
|
UIAlertAction *okAction = [UIAlertAction actionWithTitle:kOK
|
|
|
|
|
style:UIAlertActionStyleDefault
|
|
|
|
|
handler:^(UIAlertAction * _Nonnull action) {
|
|
|
|
|
UIAlertController *strongPrompt = weakPrompt;
|
|
|
|
|
completion(YES, strongPrompt.textFields[0].text);
|
|
|
|
|
}];
|
|
|
|
|
[prompt addTextFieldWithConfigurationHandler:^(UITextField *_Nonnull textField) {
|
|
|
|
|
textField.keyboardType = keyboardType;
|
|
|
|
|
}];
|
|
|
|
|
[prompt addAction:cancelAction];
|
|
|
|
|
[prompt addAction:okAction];
|
|
|
|
|
[self presentViewController:prompt animated:YES completion:nil];
|
|
|
|
|
} else {
|
|
|
|
|
SimpleTextPromptDelegate *prompt =
|
|
|
|
|
[[SimpleTextPromptDelegate alloc] initWithCompletionHandler:completion];
|
|
|
|
|
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil
|
|
|
|
|
message:message
|
|
|
|
|
delegate:prompt
|
|
|
|
|
cancelButtonTitle:@"Cancel"
|
|
|
|
|
otherButtonTitles:@"Ok", nil];
|
|
|
|
|
alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
|
|
|
|
|
[alertView show];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showSpinner:(nullable void(^)(void))completion {
|
|
|
|
|
if (self.useStatusBarSpinner) {
|
|
|
|
|
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
|
|
|
|
|
completion();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if ([self supportsAlertController]) {
|
|
|
|
|
[self showModernSpinner:completion];
|
|
|
|
|
} else {
|
|
|
|
|
[self showIOS7Spinner:completion];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showModernSpinner:(nullable void (^)(void))completion {
|
|
|
|
|
UIAlertController *pleaseWaitAlert =
|
|
|
|
|
objc_getAssociatedObject(self,
|
|
|
|
|
(__bridge const void *)kPleaseWaitAssociatedObjectKey);
|
|
|
|
|
if (pleaseWaitAlert) {
|
|
|
|
|
if (completion) {
|
|
|
|
|
completion();
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
pleaseWaitAlert = [UIAlertController alertControllerWithTitle:nil
|
|
|
|
|
message:@"Please Wait...\n\n\n\n"
|
|
|
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
|
|
|
|
|
|
UIActivityIndicatorView *spinner =
|
|
|
|
|
[[UIActivityIndicatorView alloc]
|
|
|
|
|
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
|
|
|
|
|
spinner.color = [UIColor blackColor];
|
|
|
|
|
spinner.center = CGPointMake(pleaseWaitAlert.view.bounds.size.width / 2,
|
|
|
|
|
pleaseWaitAlert.view.bounds.size.height / 2);
|
|
|
|
|
spinner.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin |
|
|
|
|
|
UIViewAutoresizingFlexibleTopMargin |
|
|
|
|
|
UIViewAutoresizingFlexibleLeftMargin |
|
|
|
|
|
UIViewAutoresizingFlexibleRightMargin;
|
|
|
|
|
[spinner startAnimating];
|
|
|
|
|
[pleaseWaitAlert.view addSubview:spinner];
|
|
|
|
|
|
|
|
|
|
objc_setAssociatedObject(self,
|
|
|
|
|
(__bridge const void *)(kPleaseWaitAssociatedObjectKey),
|
|
|
|
|
pleaseWaitAlert,
|
|
|
|
|
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
|
|
|
[self presentViewController:pleaseWaitAlert animated:YES completion:completion];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showIOS7Spinner:(nullable void (^)(void))completion {
|
|
|
|
|
UIWindow *pleaseWaitWindow =
|
|
|
|
|
objc_getAssociatedObject(self,
|
|
|
|
|
(__bridge const void *)kPleaseWaitAssociatedObjectKey);
|
|
|
|
|
|
|
|
|
|
if (pleaseWaitWindow) {
|
|
|
|
|
if (completion) {
|
|
|
|
|
completion();
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pleaseWaitWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
|
|
|
pleaseWaitWindow.backgroundColor = [UIColor clearColor];
|
|
|
|
|
pleaseWaitWindow.windowLevel = UIWindowLevelStatusBar - 1;
|
|
|
|
|
|
|
|
|
|
UIView *pleaseWaitView = [[UIView alloc] initWithFrame:pleaseWaitWindow.bounds];
|
|
|
|
|
pleaseWaitView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
|
|
|
|
|
UIViewAutoresizingFlexibleHeight;
|
|
|
|
|
pleaseWaitView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
|
|
|
|
|
UIActivityIndicatorView *spinner =
|
|
|
|
|
[[UIActivityIndicatorView alloc]
|
|
|
|
|
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
|
|
|
|
spinner.center = pleaseWaitView.center;
|
|
|
|
|
[pleaseWaitView addSubview:spinner];
|
|
|
|
|
[spinner startAnimating];
|
|
|
|
|
|
|
|
|
|
pleaseWaitView.layer.opacity = 0.0;
|
|
|
|
|
[self.view addSubview:pleaseWaitView];
|
|
|
|
|
|
|
|
|
|
[pleaseWaitWindow addSubview:pleaseWaitView];
|
|
|
|
|
|
|
|
|
|
[pleaseWaitWindow makeKeyAndVisible];
|
|
|
|
|
|
|
|
|
|
objc_setAssociatedObject(self,
|
|
|
|
|
(__bridge const void *)(kPleaseWaitAssociatedObjectKey),
|
|
|
|
|
pleaseWaitWindow,
|
|
|
|
|
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
|
|
|
|
|
|
|
|
[UIView animateWithDuration:0.5f animations:^{
|
|
|
|
|
pleaseWaitView.layer.opacity = 1.0f;
|
|
|
|
|
} completion:^(BOOL finished) {
|
|
|
|
|
if (completion) {
|
|
|
|
|
completion();
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)hideSpinner:(nullable void(^)(void))completion {
|
|
|
|
|
if (self.useStatusBarSpinner) {
|
|
|
|
|
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
|
|
|
|
|
completion();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if ([self supportsAlertController]) {
|
|
|
|
|
[self hideModernSpinner:completion];
|
|
|
|
|
} else {
|
|
|
|
|
[self hideIOS7Spinner:completion];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)hideModernSpinner:(nullable void(^)(void))completion {
|
|
|
|
|
UIAlertController *pleaseWaitAlert =
|
|
|
|
|
objc_getAssociatedObject(self,
|
|
|
|
|
(__bridge const void *)kPleaseWaitAssociatedObjectKey);
|
|
|
|
|
|
|
|
|
|
[pleaseWaitAlert dismissViewControllerAnimated:YES completion:completion];
|
|
|
|
|
|
|
|
|
|
objc_setAssociatedObject(self,
|
|
|
|
|
(__bridge const void *)(kPleaseWaitAssociatedObjectKey),
|
|
|
|
|
nil,
|
|
|
|
|
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)hideIOS7Spinner:(nullable void(^)(void))completion {
|
|
|
|
|
UIWindow *pleaseWaitWindow =
|
|
|
|
|
objc_getAssociatedObject(self,
|
|
|
|
|
(__bridge const void *)kPleaseWaitAssociatedObjectKey);
|
|
|
|
|
|
|
|
|
|
UIView *pleaseWaitView;
|
|
|
|
|
pleaseWaitView = pleaseWaitWindow.subviews.firstObject;
|
|
|
|
|
|
|
|
|
|
[UIView animateWithDuration:0.5f animations:^{
|
|
|
|
|
pleaseWaitView.layer.opacity = 0.0f;
|
|
|
|
|
} completion:^(BOOL finished) {
|
|
|
|
|
[pleaseWaitWindow resignKeyWindow];
|
|
|
|
|
objc_setAssociatedObject(self,
|
|
|
|
|
(__bridge const void *)(kPleaseWaitAssociatedObjectKey),
|
|
|
|
|
nil,
|
|
|
|
|
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
|
|
|
if (completion) {
|
|
|
|
|
completion();
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation SimpleTextPromptDelegate {
|
|
|
|
|
AlertPromptCompletionBlock _completionHandler;
|
|
|
|
|
SimpleTextPromptDelegate *_retainedSelf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (instancetype)initWithCompletionHandler:(AlertPromptCompletionBlock)completionHandler {
|
|
|
|
|
self = [super init];
|
|
|
|
|
if (self) {
|
|
|
|
|
_completionHandler = completionHandler;
|
|
|
|
|
_retainedSelf = self;
|
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
|
|
|
|
|
if (buttonIndex == alertView.firstOtherButtonIndex) {
|
|
|
|
|
_completionHandler(YES, [alertView textFieldAtIndex:0].text);
|
|
|
|
|
} else {
|
|
|
|
|
_completionHandler(NO, nil);
|
|
|
|
|
}
|
|
|
|
|
_completionHandler = nil;
|
|
|
|
|
_retainedSelf = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|