iOS_自定義毛玻璃效果 – iPhone手機開發技術文章 iPhone軟體開發教學課程

最終效果圖:

 

 

 

 

關鍵代碼:

 

UIImage分類代碼

 

//
//  UIImage+BlurGlass.h
//  帥哥_團購
//
//  Created by beyond on 14-8-30.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//  毛玻璃效果 UIImage分類

#import 

@interface UIImage (BlurGlass)


/*
 1.白色,參數:
 透明度 0~1,  0為白,   1為深灰色
 半徑:默認30,推薦值 3   半徑值越大越模糊 ,值越小越清楚
 色彩飽和度(濃度)因子:  0是黑白灰, 9是濃彩色, 1是原色  默認1.8
 “彩度”,英文是稱Saturation,即飽和度。將無彩色的黑白灰定為0,最鮮艷定為9s,這樣大致分成十階段,讓數值和人的感官直覺一致。
 */
- (UIImage *)imgWithLightAlpha:(CGFloat)alpha radius:(CGFloat)radius colorSaturationFactor:(CGFloat)colorSaturationFactor;
// 2.封裝好,供外界調用的
- (UIImage *)imgWithBlur;


@end

 

 

//
//  UIImage+BlurGlass.m
//  帥哥_團購
//
//  Created by beyond on 14-8-30.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//

#import UIImage+BlurGlass.h
#import 

@implementation UIImage (BlurGlass)



/*
   1.白色,參數:
    透明度 0~1,  0為白,   1為深灰色
    半徑:默認30,推薦值 3   半徑值越大越模糊 ,值越小越清楚
    色彩飽和度(濃度)因子:  0是黑白灰, 9是濃彩色, 1是原色  默認1.8
    “彩度”,英文是稱Saturation,即飽和度。將無彩色的黑白灰定為0,最鮮艷定為9s,這樣大致分成十階段,讓數值和人的感官直覺一致。
 */
- (UIImage *)imgWithLightAlpha:(CGFloat)alpha radius:(CGFloat)radius colorSaturationFactor:(CGFloat)colorSaturationFactor
{
    UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:alpha];
    return [self imgBluredWithRadius:radius tintColor:tintColor saturationDeltaFactor:colorSaturationFactor maskImage:nil];
}
// 2.封裝好,供外界調用的
- (UIImage *)imgWithBlur
{
    // 調用方法1
   return [self imgWithLightAlpha:0.1 radius:3 colorSaturationFactor:1];
}




// 內部方法,核心代碼,封裝瞭毛玻璃效果 參數:半徑,顏色,色彩飽和度
- (UIImage *)imgBluredWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage
{
    
    
    CGRect imageRect = { CGPointZero, self.size };
    UIImage *effectImage = self;
    
    BOOL hasBlur = blurRadius > __FLT_EPSILON__;
    BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
    if (hasBlur || hasSaturationChange) {
        UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
        CGContextRef effectInContext = UIGraphicsGetCurrentContext();
        CGContextScaleCTM(effectInContext, 1.0, -1.0);
        CGContextTranslateCTM(effectInContext, 0, -self.size.height);
        CGContextDrawImage(effectInContext, imageRect, self.CGImage);
        
        vImage_Buffer effectInBuffer;
        effectInBuffer.data     = CGBitmapContextGetData(effectInContext);
        effectInBuffer.width    = CGBitmapContextGetWidth(effectInContext);
        effectInBuffer.height   = CGBitmapContextGetHeight(effectInContext);
        effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);
        
        UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
        CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
        vImage_Buffer effectOutBuffer;
        effectOutBuffer.data     = CGBitmapContextGetData(effectOutContext);
        effectOutBuffer.width    = CGBitmapContextGetWidth(effectOutContext);
        effectOutBuffer.height   = CGBitmapContextGetHeight(effectOutContext);
        effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);
        
        if (hasBlur) {
            CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
            NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
            if (radius % 2 != 1) {
                radius += 1; // force radius to be odd so that the three box-blur methodology works.
            }
            vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
            vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
            vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
        }
        BOOL effectImageBuffersAreSwapped = NO;
        if (hasSaturationChange) {
            CGFloat s = saturationDeltaFactor;
            CGFloat floatingPointSaturationMatrix[] = {
                0.0722 + 0.9278 * s,  0.0722 - 0.0722 * s,  0.0722 - 0.0722 * s,  0,
                0.7152 - 0.7152 * s,  0.7152 + 0.2848 * s,  0.7152 - 0.7152 * s,  0,
                0.2126 - 0.2126 * s,  0.2126 - 0.2126 * s,  0.2126 + 0.7873 * s,  0,
                0,                    0,                    0,  1,
            };
            const int32_t pisor = 256;
            NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]);
            int16_t saturationMatrix[matrixSize];
            for (NSUInteger i = 0; i < matrixSize; ++i) {
                saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * pisor);
            }
            if (hasBlur) {
                vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, pisor, NULL, NULL, kvImageNoFlags);
                effectImageBuffersAreSwapped = YES;
            }
            else {
                vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, pisor, NULL, NULL, kvImageNoFlags);
            }
        }
        if (!effectImageBuffersAreSwapped)
            effectImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        if (effectImageBuffersAreSwapped)
            effectImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }
    
    // 開啟上下文 用於輸出圖像
    UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
    CGContextRef outputContext = UIGraphicsGetCurrentContext();
    CGContextScaleCTM(outputContext, 1.0, -1.0);
    CGContextTranslateCTM(outputContext, 0, -self.size.height);
    
    // 開始畫底圖
    CGContextDrawImage(outputContext, imageRect, self.CGImage);
    
    // 開始畫模糊效果
    if (hasBlur) {
        CGContextSaveGState(outputContext);
        if (maskImage) {
            CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
        }
        CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
        CGContextRestoreGState(outputContext);
    }
    
    // 添加顏色渲染
    if (tintColor) {
        CGContextSaveGState(outputContext);
        CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
        CGContextFillRect(outputContext, imageRect);
        CGContextRestoreGState(outputContext);
    }
    
    // 輸出成品,並關閉上下文
    UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return outputImage;
}

@end

控制器代碼

 

 

//
//  MineController.m
//  帥哥_團購
//
//  Created by beyond on 14-8-14.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//  dock上面的【我的】按鈕對應的控制器

#import MineController.h
#import ImgDownloadTool.h
#import 
#import UIImage+BoxBlur.h
#import GirlCell.h
// 每一個格子的寬和高
#define kItemW 240
#define kItemH 320
@interface MineController ()
{
    NSMutableArray *_imgArr;
    UIWebView *_webView;
    
    // 添加一個coverImgView,用於點擊瞭cell時,進行屏幕截圖並加上毛玻璃效果,置於最上方作為蒙板
    
    UIImageView *_coverBlurImgView;
    // 點擊cell,彈出一個大圖(必須在控制器顯示之前 再確定frame,真實的frame)
    UIImageView *_showingImgView;

}

@end

@implementation MineController


#pragma mark - 生命周期方法

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @我的青春誰做主;
    // 0.加載plist文件保存的url數組
    // sg_bundle模板代碼,1,獲得.app主要的包;2,返回主要的包中某個文件的fullPath全路徑
    NSBundle *mainBundle = [NSBundle mainBundle];
    NSString *fullPath = [mainBundle pathForResource:@sinaImgArr.plist ofType:nil];
    _imgArr = [NSArray arrayWithContentsOfFile:fullPath];
    
    
    
    
    // 1.創建自己的collectionView
    [self addCollectionView];
    
    // 2.註冊cell格子要用到的xib文件
    [self.collectionView registerNib:[UINib nibWithNibName:@GirlCell bundle:nil] forCellWithReuseIdentifier:@GirlCell];
    
    // 3.設置collectionView永遠支持垂直滾動,為下拉刷新準備(彈簧)
    self.collectionView.alwaysBounceVertical = YES;
    
    // 4.設置collectionView的背景色
    self.collectionView.backgroundColor = kGlobalBg;
    
    // 5.添加一個coverImgView,用於點擊瞭cell時,進行屏幕截圖並加上毛玻璃效果,置於最上方作為蒙板
    _coverBlurImgView = [[UIImageView alloc]init];
    [self.view addSubview:_coverBlurImgView];
    
    
    // 6.點擊cell,彈出一個大圖(必須在控制器顯示之前 再確定frame,真實的frame)
    _showingImgView = [[UIImageView alloc]init];
    _showingImgView.backgroundColor = [UIColor clearColor];
    [self.view addSubview:_showingImgView];
    _showingImgView.contentMode = UIViewContentModeScaleAspectFit;
    _showingImgView.userInteractionEnabled = YES;
    [_showingImgView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showingImgTap)]];

}

// 1.創建自己的collectionView
- (void)addCollectionView
{
    // 創建一個流佈局,必須指定
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    // 設置流佈局裡面的每一個格子寬和高,即每一個網格的尺寸
    layout.itemSize = CGSizeMake(kItemW, kItemH);
    // 每一行之間的間距
    layout.minimumLineSpacing = 20;
    // 指定的流佈局創建一個collectionView,並且用成員變量記住
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    // 高度和寬度自動伸縮
    self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    [self.view addSubview:self.collectionView];
}
#pragma mark 在viewWillAppear和viewDidAppear中可以取得view最準確的寬高(width和height)
// 重要~~~因為在控制器創建時,寬默認是768,高默認是1024,不管橫豎屏
// 隻有在viewWillAppear和viewDidAppear方法中,可以取得view最準確的(即實際的)寬和高(width和height)
- (void)viewWillAppear:(BOOL)animated
{
    // 默認計算layout
    [self didRotateFromInterfaceOrientation:0];
}
#pragma mark - 父類方法

// 攔截,屏幕即將旋轉的時候調用(控制器監控屏幕旋轉)
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    //log(@屏幕即將旋轉);
}


#pragma mark 屏幕旋轉完畢的時候調用
// 攔截,屏幕旋轉完畢的時候調用
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    // 1.取出創建CollectionViewController時傳入的的UICollectionViewFlowLayout
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
    
    
    // 2.計算間距
    CGFloat v = 0;
    CGFloat h = 0;
    CGFloat height = self.view.frame.size.height -44;
    CGFloat width = self.view.frame.size.width;
    if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)
        ) {
        // 橫屏的間距
        v = (height - 2 * kItemH) / 3;
        h = (width - 3 * kItemW) / 4;
        
    } else {
        // 豎屏的間距
        v = (height - 3 * kItemH) / 4;
        h = (width - 2 * kItemW) / 3;
    }
    // 3.動畫調整格子之間的距離
    [UIView animateWithDuration:4.0 animations:^{
        // 上 左 下 右 四個方向的margin
        layout.sectionInset = UIEdgeInsetsMake(h, h, v, h);
        // 每一行之間的間距
        layout.minimumLineSpacing = h;
    }];
    
    // 4.旋轉完成之後,才可以得到真實的frame,暫時隱藏起來,當點擊cell的時侯才展示  -5
    _coverBlurImgView.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
    _coverBlurImgView.hidden = YES;
    
    
    _showingImgView.hidden = YES;
    CGRect temp =     _showingImgView.frame;
    CGFloat x =self.view.frame.size.width * 0.5;
    CGFloat y =self.view.frame.size.height * 0.5;
    temp = CGRectMake(x,y, 0, 0);
    _showingImgView.frame = temp;
}

#pragma mark - collectionView代理方法
// 共有多少個Item(就是格子Cube),詢問子類
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return _imgArr.count;
}
#pragma mark 刷新數據的時候會調用(reloadData)
#pragma mark 每當有一個cell重新進入屏幕視野范圍內就會調用
// 生成每一個獨一無二的格子,詢問子類
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @GirlCell;
    GirlCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
    
    cell.imgSrc = _imgArr[indexPath.row];
    
    return cell;
}
// 點擊瞭一個格子時,1,截屏,2,動畫毛玻璃圖片,3,showing從小放到大
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    
    // 1,截屏
    [self screenShot];
    // 2,顯示
    _coverBlurImgView.alpha = 1; 
    _coverBlurImgView.hidden = NO;
    
    // 3.調用UIImage的分類方法,進行毛玻璃處理
    _coverBlurImgView.image = [_coverBlurImgView.image imgWithBlur];

    // 4.展示_showingImgView
    _showingImgView.hidden = NO;
    
    
    // 點擊cell,彈出一個大圖
    CGRect temp =     _showingImgView.frame;
    CGFloat x =self.view.frame.size.width * 0.5;
    CGFloat y =self.view.frame.size.height * 0.5;
    temp = CGRectMake(x,y, 0, 0);
    _showingImgView.frame = temp;
    _showingImgView.alpha = 0;
    [UIView animateWithDuration:0.5 animations:^{
          [ImgDownloadTool imgDownloadWithUrl:_imgArr[indexPath.row] tmpImgName:kImgPlaceHolder imageView:_showingImgView];
        _showingImgView.alpha = 1;
        _showingImgView.frame = self.view.bounds;
    }];
    
}


// 使用上下文截圖,並使用指定的區域裁剪,模板代碼
- (void)screenShot
{
    // 將要被截圖的view
    // 背景圖片 總的大小
    CGSize size = self.view.frame.size;
    UIGraphicsBeginImageContext(size);
    // 開啟上下文,使用參數之後,截出來的是原圖(YES  0.0 質量高)
    UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);

    // 裁剪的關鍵代碼,要裁剪的矩形范圍
    CGRect rect = CGRectMake(0, 0, size.width, size.height  );
    //註:iOS7以後renderInContext:由drawViewHierarchyInRect:afterScreenUpdates:替代
    [self.view drawViewHierarchyInRect:rect  afterScreenUpdates:NO];
    // 從上下文中,取出UIImage
    UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
    // 添加截取好的圖片到圖片View裡面
    _coverBlurImgView.image = snapshot;
    
    // 千萬記得,結束上下文(移除棧頂上下文)
    UIGraphicsEndImageContext();
    
}

// 正在顯示的大圖被點瞭
- (void)showingImgTap
{
    
    
    
    [UIView animateWithDuration:0.5 animations:^{
        CGRect temp =     _showingImgView.frame;
        CGFloat x =self.view.frame.size.width * 0.5;
        CGFloat y =self.view.frame.size.height * 0.5;
        temp = CGRectMake(x,y, 0, 0);
        _showingImgView.frame = temp;
        _showingImgView.alpha = 0;
    } completion:^(BOOL finished) {
        // 隱藏起來
        _showingImgView.hidden = YES;
        
        _coverBlurImgView.hidden = YES;
    }];
}

#pragma mark - 生命周期方法
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end

 

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *