iOSでUIWebViewのスクリーンショットを取る方法


■問題

iOSでisLoadingが常にYESを返す場合があり、完全にロードしたかどうかを判別できない場合があります。

■解決策

webViewDidFinishLoadが呼ばれた時点で、スクリーンショットを取り、白・黒の割合が90%未満だった場合にはロードされていると認識する。
※この方法では完全にとれない場合もあります。
※100%ロードしたものを表示したい場合には、timer等で以下の関数を処理を実行させて下さい。

■予備知識

UIViewからサムネイルを取得する方法
http://araking56.blog134.fc2.com/blog-entry-184.html

画像のピクセルから色を判別する方法
http://www.markj.net/iphone-uiimage-pixel-color/

■good

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    
    BOOL isLoaded = [self isLoaded:webView];
    if(isLoaded){
        [self.imageView setHidden:NO];
    }
}

-(BOOL)isLoaded:(UIWebView *)webView{
    
    //画像を送る
    CGRect screenRect = webView.frame;
    UIGraphicsBeginImageContext(webView.frame.size);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextFillRect(ctx, screenRect);
    [webView.layer renderInContext:ctx];
    
    UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    //画像の内容を解析する
    float percentage = [self getPercentageOfWhiteInImage:screenImage];
    
    if (percentage > 0.9) {
        
        NSLog(@"ほぼ真っ白 or 真っ黒なのでロードされていない");
        
        
    }else{
        
        NSLog(@"色がついているのでロード済み");
        
    }
}

-(float)getPercentageOfWhiteInImage:(UIImage *)image{
    
    CGImageRef inImage = image.CGImage;
    CGContextRef cgctx = [self createARGBBitmapContextFromImage:inImage];
    if (cgctx == NULL) { return 1.0; /* error */ }
    
    size_t w = CGImageGetWidth(inImage);
    size_t h = CGImageGetHeight(inImage);
    CGRect rect = {{0,0},{w,h}}; 
    CGContextDrawImage(cgctx, rect, inImage);
    
    int startW = 0;
    int startH = 0;
    unsigned char* data = CGBitmapContextGetData (cgctx);
    
    float result1 = [self getPercentageOfWhiteInPix:startW startH:startH data:data w:w h:h];
    float result2 = [self getPercentageOfWhiteInPix:w/2-_estimateLengthstartH:h/2-_estimateLengthdata:data w:w h:h];
    float result3 = [self getPercentageOfWhiteInPix:w-_estimateLengthstartH:h-_estimateLengthdata:data w:w h:h];
    
    // When finished, release the context
    CGContextRelease(cgctx);
    // Free image data memory for the context
    if (data) { free(data); }
    
    return (result1+result2+result3)/(float)3;
    
}

-(float)getPercentageOfWhiteInPix:(int)startW startH:(int)startH data:(unsigned char *)data w:(size_t)w h:(size_t)h{
    
    // Now we can get a pointer to the image data associated with the bitmap
    // context.
    int whiteCounter =0;
    int blackCounter = 0;
    int notWhiteCounter = 0;
    //unsigned char* data = CGBitmapContextGetData (cgctx);
    for (int width =0; width < _estimateLength ; width++) {
        
        for (int height=0;height < _estimateLength; height++) {
            
            if (data != NULL) {
                
                //offset locates the pixel in the data from x,y.
                //4 for 4 bytes of data per pixel, w is width of one row of data.
                int offset = 4*((w*round(startH+height))+round(startW+width));
                int alpha =  data[offset];
                int red = data[offset+1];
                int green = data[offset+2];
                int blue = data[offset+3];
                
                if (red == 255 && green == 255 && blue == 255) {
                    whiteCounter++;
                }else if(red ==0 && green==0 && blue==0){
                    blackCounter++;
                }else {
                    notWhiteCounter++;
                }
                NSLog(@"offset: %i colors: RGB A %i %i %i  %i",offset,red,green,blue,alpha);
                //color = [UIColor colorWithRed:(red/255.0f) green:(green/255.0f) blue:(blue/255.0f) alpha:(alpha/255.0f)];
                
            }
            
        }
        
    }
    
    //大きい方を返す
    
    if (whiteCounter > blackCounter) {
        
        return (float)whiteCounter/(float)(notWhiteCounter+whiteCounter+blackCounter);
        
    }else {
        
        return (float)blackCounter/(float)(notWhiteCounter+whiteCounter+blackCounter);
        
    }
    
}

- (CGContextRef) createARGBBitmapContextFromImage:(CGImageRef) inImage {
    
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int            bitmapByteCount;
    int            bitmapBytesPerRow;
    
    size_t pixelsWide = CGImageGetWidth(inImage);
    size_t pixelsHigh = CGImageGetHeight(inImage);
    
    bitmapBytesPerRow   = (pixelsWide * 4);
    bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);
    
    colorSpace = CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL)
    {
        fprintf(stderr, "Error allocating color space\n");
        return NULL;
    }
    
    bitmapData = malloc( bitmapByteCount );
    if (bitmapData == NULL)
    {
        fprintf (stderr, "Memory not allocated!");
        CGColorSpaceRelease( colorSpace );
        return NULL;
    }
    
    context = CGBitmapContextCreate (bitmapData,
                                     pixelsWide,
                                     pixelsHigh,
                                     8,      // bits per component
                                     bitmapBytesPerRow,
                                     colorSpace,
                                     kCGImageAlphaPremultipliedFirst);
    if (context == NULL)
    {
        free (bitmapData);
        fprintf (stderr, "Context not created!");
    }
    
    // Make sure and release colorspace before returning
    CGColorSpaceRelease( colorSpace );
    
    return context;
}

■本ブログに関連する検索結果を見るには、アトサクが便利です。

Obective-c UIView サムネイル
Objective-C 画像 ピクセル 色 取得
(ボタンをクリックすると、キーワードが保管され後でまとめて読む事ができます。)
アトサクについては、本家ページをご参照下さい。
[sc:採用情報へのリンク]