Paradigm Shift Design

ISHITOYA Kentaro's blog.

MFMailComposeViewControllerの確認画面を超絶無視する方法

MFMailComposeViewControllerの確認画面がうざい。
一律に、確認しないとメールを送れないようにするんじゃなくて、所定の手続きを踏んでユーザに確認すれば、確認画面を出さないでバックグラウンドでメールを送れるようにして欲しい...

で、skpsmtpmessage Quick SMTP client code for the iPhone を使おうかと思ったけど、デフォルトで設定されているE-Mailアカウントを使えない。
それじゃ意味がなさすぎるので。

なんか方法ないかなーってさまよってたら、cocoa touch - iPhone send email not using MessageUI - Stack Overflowというのを見つけた。Sendボタン押しちゃうわけね。絶対、審査とおんないんだろうなぁ。

通んないのかなぁ。とりあえず投げてみてから考えるか。っつーわけで、実装してみた。

まず、MFMailComposeViewControllerの設定。ちなみにSimulatorではメールを送れない。あと、サブジェクトがないとAlertが出てうざいので、ちゃんと埋めておく。

/*!
 * submit content
 */
- (id)submitContent:(PhotoSubmitterContentEntity *)content 
      andOperationDelegate:(id<PhotoSubmitterPhotoOperationDelegate>)delegate{
    MFMailComposeViewController *mailComposeViewController = [[MFMailComposeViewController alloc] init];
    if([MFMailComposeViewController canSendMail] == NO){
        [self completeSubmitContentWithRequest:mailComposeViewController andError:nil];
        return mailComposeViewController;
    }
    
    mailComposeViewController.mailComposeDelegate = self;
    [mailComposeViewController setToRecipients:[NSArray arrayWithObject:self.sendTo]];
    
    NSString *subject = nil;
    NSString *body = nil;
    if(content.comment == nil && self.defaultSubject != nil){
        subject = self.defaultSubject;
    }
    if(content.comment != nil && self.commentAsSubject){
        subject = content.comment;
    }
    if(content.comment == nil && self.defaultBody != nil){
        body = self.defaultBody;
    }
    if(content.comment != nil && self.commentAsBody){
        body = content.comment;
    }
    
    if(subject == nil || [subject isEqualToString:@""]){
        subject = @"tottepost photo";
    }
    [mailComposeViewController setSubject:subject];
    [mailComposeViewController setMessageBody:body isHTML:NO];
    
    
    NSDateFormatter* dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateFormat:@"M/d/y h:m:s"];
    
    NSDateFormatter *df = [[NSDateFormatter alloc] init];
    df.dateFormat  = @"yyyyMMddHHmmssSSSS";
    NSString *filename = nil;
    NSString *mimeType = nil;
    if(content.isPhoto){
        filename = [NSString stringWithFormat:@"%@.jpg", 
                     [df stringFromDate:content.timestamp]];
        mimeType = @"image/jpeg";
    }else{
        filename = [NSString stringWithFormat:@"%@.mp4", 
                     [df stringFromDate:content.timestamp]];
        mimeType = @"video/mp4";
    }
    
    [mailComposeViewController addAttachmentData:content.data 
                                        mimeType:mimeType fileName:filename];
    if(self.confirm == NO){
        [mailComposeViewController view];
    }else{     
        dispatch_async(dispatch_get_main_queue(),^{
            [self presentModalViewController:mailComposeViewController];
        });
    }
    return mailComposeViewController;
}


で、composeし終わってMFMailComposeViewControllerが表示されそうになったら、controllerのsubviewを辿ってsendボタンを探す。探すのは再帰になるのでこんな感じのメソッドを作る。

/*!
 * find views named
 */
- (NSArray *)searchForViewsNamed:(NSString *)name 
                        fromView:(UIView *)view found:(NSMutableArray *)found{
    if(found == nil){
        found = [[NSMutableArray alloc] init];
    }
    
    for (UIView *subview in [view subviews]) {
        if([NSStringFromClass([subview class]) isEqualToString:name]){
            [found addObject:subview];
        }
        [self searchForViewsNamed:name fromView:subview found:found];
    }
    return found;
}


でUINavigationBarの右側にあるSendボタンを探すので、こうなる。

/*!
 * on composed
 */
- (void) mailComposeController:(MFMailComposeViewController*)controller 
 bodyFinishedLoadingWithResult:(NSInteger)result error:(NSError*)error
{
    if(self.confirm == NO){
        id button = nil;
        NSArray *views = 
          [self searchForViewsNamed:@"UINavigationBar" 
                           fromView:controller.view found:nil];
        if(views.count == 0){
            return;
        }
        views = [self searchForViewsNamed:@"UINavigationButton" 
                                 fromView:[views objectAtIndex:0] found:nil];
        if(views.count == 0){
            return;
        }
        for (UIView *v in views) {
            if(v.frame.origin.x > 200){
                button = v;
            }
        }
        
        if(button == nil){
            return;
        }
        [button sendActionsForControlEvents:UIControlEventTouchUpInside];
        [self checkForSizeConfirmWindow];
    }
}


でもって、sendボタン押した後、写真のサイズが大きすぎると、選択するUIActionSheetが出てしまう。出ている場合にはUIApplicationのkeyWindowからUIActionSheetを辿れるので、適当に選択するようにする。

/*!
 * select size confirm button
 */
- (void) checkForSizeConfirmWindow{
    UIActionSheet *actionsheet = nil;
    NSArray *buttons = [[NSMutableArray alloc] init];
    NSArray *views = 
      [self searchForViewsNamed:@"UIActionSheet" 
                       fromView:[UIApplication sharedApplication].keyWindow found:nil];
    if(views.count == 0){
        return;
    }
    actionsheet = [views objectAtIndex:0];
    buttons = [self searchForViewsNamed:@"UIAlertButton" 
                               fromView:actionsheet found:nil];
    if(actionsheet && buttons.count > 2){
        [[buttons objectAtIndex:1] sendActionsForControlEvents:UIControlEventTouchUpInside];
    }
}

で、これで黙ってSendできます。多分審査は通らないと思うけど、とりあえず出してみる。まぁ、確認画面が毎回出るだけなんだけど。

当然、AppStore通さないアプリなら問題なく使えます。

追記

審査とおった。多分、運だと思います。

せんでん

1タップで写真&動画を共有できるカメラtottepost
カメラのついたiPad/iPhoneで撮った写真や動画を、その場でFacebook/Mixi/DropboxなどのサービスにアップロードできるtottepostというiOSアプリを開発しています!詳しくは、iTunes App Storeをご覧ください。


ご購入はこちら!
1タップで写真&動画を共有できるカメラ - tottepost - ISHITOYA Kentaro