blob: 0c9031a32316c3bc3ad014a30fbd6ba3729bb4bd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 "WXModalUIModule.h"
#import <objc/runtime.h>
#import "WXUtility.h"
#import "WXAssert.h"
static NSString *WXModalCallbackKey;
typedef enum : NSUInteger {
WXModalTypeToast = 1,
WXModalTypeAlert,
WXModalTypeConfirm,
WXModalTypePrompt
} WXModalType;
@interface WXToastInfo : NSObject
@property (nonatomic, strong) UIView *toastView;
@property (nonatomic, weak) UIView *superView;
@property (nonatomic, assign) double duration;
@end
@implementation WXToastInfo
@end
@interface WXToastManager : NSObject
@property (strong, nonatomic) NSMutableArray<WXToastInfo *> *toastQueue;
@property (strong, nonatomic) UIView *toastingView;
+ (WXToastManager *)sharedManager;
@end
@implementation WXToastManager
+ (WXToastManager *)sharedManager{
static WXToastManager * shareInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareInstance = [[WXToastManager alloc] init];
shareInstance.toastQueue = [NSMutableArray new];
});
return shareInstance;
}
@end
@interface WXModalUIModule () <UIAlertViewDelegate>
@end
@implementation WXModalUIModule
{
NSMutableSet *_alertViews;
}
@synthesize weexInstance;
WX_EXPORT_METHOD(@selector(toast:))
WX_EXPORT_METHOD(@selector(alert:callback:))
WX_EXPORT_METHOD(@selector(confirm:callback:))
WX_EXPORT_METHOD(@selector(prompt:callback:))
- (instancetype)init
{
if (self = [super init]) {
_alertViews = [NSMutableSet setWithCapacity:1];
}
return self;
}
- (void)dealloc
{
if (WX_SYS_VERSION_LESS_THAN(@"8.0")) {
for (UIAlertView *alerView in _alertViews) {
alerView.delegate = nil;
}
}
[_alertViews removeAllObjects];
}
#pragma mark - Toast
static const double WXToastDefaultDuration = 3.0;
static const CGFloat WXToastDefaultFontSize = 16.0;
static const CGFloat WXToastDefaultWidth = 230.0;
static const CGFloat WXToastDefaultHeight = 30.0;
static const CGFloat WXToastDefaultPadding = 30.0;
- (void)toast:(NSDictionary *)param
{
NSString *message = [self stringValue:param[@"message"]];
if (!message) return;
double duration = [param[@"duration"] doubleValue];
if (duration <= 0) {
duration = WXToastDefaultDuration;
}
WXPerformBlockOnMainThread(^{
[self toast:message duration:duration];
});
}
- (void)toast:(NSString *)message duration:(double)duration
{
WXAssertMainThread();
UIView *superView = self.weexInstance.rootView.window;
if (!superView) {
superView = self.weexInstance.rootView;
}
UIView *toastView = [self toastViewForMessage:message superView:superView];
WXToastInfo *info = [WXToastInfo new];
info.toastView = toastView;
info.superView = superView;
info.duration = duration;
[[WXToastManager sharedManager].toastQueue addObject:info];
if (![WXToastManager sharedManager].toastingView) {
[self showToast:toastView superView:superView duration:duration];
}
}
- (UIView *)toastViewForMessage:(NSString *)message superView:(UIView *)superView
{
CGFloat padding = WXToastDefaultPadding;
UILabel *messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(padding/2, padding/2, WXToastDefaultWidth, WXToastDefaultHeight)];
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = NSTextAlignmentCenter;
messageLabel.text = message;
messageLabel.font = [UIFont boldSystemFontOfSize:WXToastDefaultFontSize];
messageLabel.lineBreakMode = NSLineBreakByTruncatingTail;
messageLabel.textColor = [UIColor whiteColor];
messageLabel.backgroundColor = [UIColor clearColor];
[messageLabel sizeToFit];
UIView *toastView = [[UIView alloc] initWithFrame:
CGRectMake(
(superView.frame.size.width-messageLabel.frame.size.width-padding)/2,
(superView.frame.size.height-messageLabel.frame.size.height-padding)/2,
messageLabel.frame.size.width+padding,
messageLabel.frame.size.height+padding
)];
CGPoint point = CGPointZero;
UIWindow *window = [[[UIApplication sharedApplication] windows] objectAtIndex:0];
// adjust to screen orientation
UIInterfaceOrientation orientation = (UIInterfaceOrientation)[[UIApplication sharedApplication] statusBarOrientation];
switch (orientation) {
case UIDeviceOrientationPortrait: {
point = CGPointMake(window.frame.size.width/2, window.frame.size.height/2);
break;
}
case UIDeviceOrientationPortraitUpsideDown: {
toastView.transform = CGAffineTransformMakeRotation(M_PI);
float width = window.frame.size.width;
float height = window.frame.size.height;
point = CGPointMake(width/2, height/2);
break;
}
case UIDeviceOrientationLandscapeLeft: {
toastView.transform = CGAffineTransformMakeRotation(M_PI/2); //rotation in radians
point = CGPointMake(window.frame.size.width/2, window.frame.size.height/2);
break;
}
case UIDeviceOrientationLandscapeRight: {
toastView.transform = CGAffineTransformMakeRotation(-M_PI/2);
point = CGPointMake(window.frame.size.width/2, window.frame.size.height/2);
break;
}
default:
break;
}
toastView.center = point;
toastView.frame = CGRectIntegral(toastView.frame);
[toastView addSubview:messageLabel];
toastView.layer.cornerRadius = 7;
toastView.backgroundColor=[UIColor colorWithWhite:0 alpha:0.7];
return toastView;
}
- (void)showToast:(UIView *)toastView superView:(UIView *)superView duration:(double)duration
{
if (!toastView || !superView) {
return;
}
[WXToastManager sharedManager].toastingView = toastView;
[superView addSubview:toastView];
__weak typeof(self) weakSelf = self;
[UIView animateWithDuration:0.2 delay:duration options:UIViewAnimationOptionCurveEaseInOut animations:^{
toastView.transform = CGAffineTransformConcat(toastView.transform, CGAffineTransformMakeScale(0.8, 0.8)) ;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.2 delay:0.2 options:UIViewAnimationOptionCurveEaseInOut animations:^{
toastView.alpha = 0;
} completion:^(BOOL finished){
[toastView removeFromSuperview];
[WXToastManager sharedManager].toastingView = nil;
NSMutableArray *queue = [WXToastManager sharedManager].toastQueue;
if (queue.count > 0) {
[queue removeObjectAtIndex:0];
if (queue.count > 0) {
WXToastInfo *info = [queue firstObject];
[weakSelf showToast:info.toastView superView:info.superView duration:info.duration];
}
}
}];
}];
}
#pragma mark - Alert
- (void)alert:(NSDictionary *)param callback:(WXModuleKeepAliveCallback)callback
{
NSString *message = [self stringValue:param[@"message"]];
NSString *okTitle = [self stringValue:param[@"okTitle"]];
if ([WXUtility isBlankString:okTitle]) {
okTitle = @"OK";
}
[self alert:message okTitle:nil cancelTitle:okTitle defaultText:nil type:WXModalTypeAlert callback:callback];
}
#pragma mark - Confirm
- (void)confirm:(NSDictionary *)param callback:(WXModuleKeepAliveCallback)callback
{
NSString *message = [self stringValue:param[@"message"]];
NSString *okTitle = [self stringValue:param[@"okTitle"]];
NSString *cancelTitle = [self stringValue:param[@"cancelTitle"]];
if ([WXUtility isBlankString:okTitle]) {
okTitle = @"OK";
}
if ([WXUtility isBlankString:cancelTitle]) {
cancelTitle = @"Cancel";
}
[self alert:message okTitle:okTitle cancelTitle:cancelTitle defaultText:nil type:WXModalTypeConfirm callback:callback];
}
#pragma mark - Prompt
- (void)prompt:(NSDictionary *)param callback:(WXModuleKeepAliveCallback)callback
{
NSString *message = [self stringValue:param[@"message"]];
NSString *defaultValue = [self stringValue:param[@"default"]];
NSString *okTitle = [self stringValue:param[@"okTitle"]];
NSString *cancelTitle = [self stringValue:param[@"cancelTitle"]];
if ([WXUtility isBlankString:okTitle]) {
okTitle = @"OK";
}
if ([WXUtility isBlankString:cancelTitle]) {
cancelTitle = @"Cancel";
}
[self alert:message okTitle:okTitle cancelTitle:cancelTitle defaultText:defaultValue type:WXModalTypePrompt callback:callback];
}
#pragma mark - Private
- (void)alert:(NSString *)message okTitle:(NSString *)okTitle cancelTitle:(NSString *)cancelTitle defaultText:(NSString *)defaultText type:(WXModalType)type callback:(WXModuleKeepAliveCallback)callback
{
if (!message) {
if (callback) {
callback(@"Error: message should be passed correctly.",NO);
}
return;
}
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"" message:message delegate:self cancelButtonTitle:cancelTitle otherButtonTitles:okTitle, nil];
alertView.tag = type;
if (type == WXModalTypePrompt) {
alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
UITextField *textField = [alertView textFieldAtIndex:0];
textField.placeholder = defaultText;
}
objc_setAssociatedObject(alertView, &WXModalCallbackKey, [callback copy], OBJC_ASSOCIATION_COPY_NONATOMIC);
[_alertViews addObject:alertView];
WXPerformBlockOnMainThread(^{
[alertView show];
});
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
WXModuleKeepAliveCallback callback = objc_getAssociatedObject(alertView, &WXModalCallbackKey);
if (!callback) return;
id result = @"";
switch (alertView.tag) {
case WXModalTypeAlert: {
result = @"";
break;
}
case WXModalTypeConfirm: {
NSString *clickTitle = [alertView buttonTitleAtIndex:buttonIndex];
result = clickTitle;
break;
}
case WXModalTypePrompt: {
NSString *clickTitle = [alertView buttonTitleAtIndex:buttonIndex];
NSString *text= [[alertView textFieldAtIndex:0] text] ?: @"";
result = @{ @"result": clickTitle, @"data": text };
break;
}
default:
break;
}
callback(result,NO);
[_alertViews removeObject:alertView];
}
- (NSString*)stringValue:(id)value
{
if ([value isKindOfClass:[NSString class]]) {
return value;
}
if ([value isKindOfClass:[NSNumber class]]) {
return [value stringValue];
}
return nil;
}
@end