JPImageresizerView - 專(zhuān)門(mén)裁剪圖片、GIF支救、視頻的輪子(簡(jiǎn)單易用抢野、功能豐富)

效果圖

簡(jiǎn)介(當(dāng)前版本:1.11.4)

一個(gè)專(zhuān)門(mén)裁剪圖片、GIF各墨、視頻的輪子指孤,簡(jiǎn)單易用,功能豐富(高自由度的參數(shù)設(shè)定贬堵、支持旋轉(zhuǎn)和鏡像翻轉(zhuǎn)恃轩、蒙版、壓縮等)黎做,能滿(mǎn)足絕大部分裁剪的需求叉跛。

Feature:
    ? 能自適應(yīng)裁剪區(qū)域的縮放;
    ? 高自由度的參數(shù)設(shè)定蒸殿,包括裁剪區(qū)域的間距筷厘、裁剪寬高比、是否自適應(yīng)縮放等宏所;
    ? 支持最多8個(gè)拖拽方向的裁剪區(qū)域酥艳;
    ? 支持上左下右的旋轉(zhuǎn);
    ? 水平和垂直的鏡像翻轉(zhuǎn)爬骤;
    ? 兩種邊框樣式充石;
    ? 支持圓框裁剪;
    ? 自定義毛玻璃樣式盖腕、邊框顏色赫冬、背景顏色、遮罩透明度溃列;
    ? 自定義邊框圖片;
    ? 可動(dòng)態(tài)修改視圖區(qū)域和裁剪區(qū)域間距劲厌,支持橫豎屏切換;
    ? 可自定義蒙版圖片裁剪;
    ? 可裁剪本地視頻整段畫(huà)面或某一幀畫(huà)面听隐;
    ? 可截取某一段本地視頻补鼻,裁剪后并轉(zhuǎn)成GIF;
    ? 可裁剪GIF雅任;
    ? 可保存當(dāng)前裁剪狀態(tài)风范;
    ? 圖片支持N宮格裁剪;
    ? 兼容Swift&SwiftUI環(huán)境沪么。

TODO:
    ?? Swift版本硼婿;
    ?? 固定不縮放裁剪區(qū)域;
    ?? 視頻不再需要修正方向再裁剪禽车;
    ?? 裁剪遠(yuǎn)程視頻寇漫;
    ?? 持久化緩存裁剪歷史刊殉;
    ?? 將視頻裁剪部分(AVFoundation模塊)分離出來(lái);
    ?? 實(shí)現(xiàn)蘋(píng)果相冊(cè)裁剪功能中的自由拖拽旋轉(zhuǎn)州胳、翻轉(zhuǎn)角度的效果记焊。

注意:由于autoLayout不利于手勢(shì)控制,所以目前使用的是frame布局栓撞,暫不支持autoLayout遍膜。

最新改動(dòng)

兼容Swift&SwiftUI環(huán)境。

如何使用

初始化

1. 配置初始參數(shù)

可設(shè)置的裁剪元素(圖片瓤湘、GIF瓢颅、視頻),只能選擇其中一個(gè)岭粤,并且不能為nil:
    - image:裁剪的圖片/GIF(以UIImage傳入)
    - imageData:裁剪的圖片/GIF(以NSData傳入)
    - videoURL:裁剪的本地視頻(以NSURL傳入)
    - videoAsset:裁剪的本地視頻(以AVURLAsset傳入)
    
其他部分可配置參數(shù)(更多可查看JPImageresizerView的頭文件):
    - blurEffect:毛玻璃樣式
    - borderImage:邊框圖片
    - frameType & strokeColor:邊框樣式&顏色
    - bgColor:背景色
    - maskAlpha:遮罩透明度
    - resizeWHScale:裁剪的寬高比
    - contentInsets:裁剪區(qū)域與視圖的間距
    - maskImage:蒙版圖片

圖片/GIF

//【裁剪的圖片/GIF】以UIImage傳入
JPImageresizerConfigure *configure = [JPImageresizerConfigure defaultConfigureWithImage:image make:^(JPImageresizerConfigure *configure) {
    // 到這里已經(jīng)有了默認(rèn)參數(shù)值惜索,可以在這里另外設(shè)置你想要的參數(shù)值(使用了鏈?zhǔn)骄幊谭绞剑?    configure
    .jp_maskAlpha(0.5)
    .jp_strokeColor([UIColor yellowColor])
    .jp_frameType(JPClassicFrameType)
    .jp_contentInsets(contentInsets)
    .jp_bgColor([UIColor orangeColor])
    .jp_isClockwiseRotation(YES)
    .jp_animationCurve(JPAnimationCurveEaseOut);
}];

// 如果想要初始化為正方形,可設(shè)置 JPImageresizerConfigure 的 resizeWHScale 屬性
configure.resizeWHScale = 1; // 默認(rèn)為0剃浇,完全顯示
// 另外如果還需要固定比例的話(huà):
configure.isArbitrarily = YES; // 默認(rèn)為YES

// 2.【裁剪的圖片/GIF】以NSData傳入
JPImageresizerConfigure *configure = [JPImageresizerConfigure defaultConfigureWithImageData:imageData make:^(JPImageresizerConfigure *configure) { ...... };

視頻

關(guān)于從系統(tǒng)相冊(cè)獲取的視頻巾兆,視頻方向有可能是修改過(guò)的(即相冊(cè)中旋轉(zhuǎn)、翻轉(zhuǎn)過(guò))虎囚,修改后的videoTrack.preferredTransform != CGAffineTransformIdentity角塑,圖片也會(huì),不過(guò)好歹圖片有個(gè)imageOrientation屬性告知具體改動(dòng)了什么淘讥,由于我才疏學(xué)淺圃伶,單單從preferredTransform并不知道是經(jīng)過(guò)了具體的哪些改動(dòng),如果只是旋轉(zhuǎn)還好蒲列,旋轉(zhuǎn)+翻轉(zhuǎn)后的數(shù)值都是不一定的窒朋,這樣導(dǎo)致最后裁剪時(shí)會(huì)錯(cuò)亂,目前只好先修正方向后再進(jìn)行裁剪蝗岖,日后改進(jìn)侥猩,希望能有緣之士給予指點(diǎn)!

初始化后再修正(先進(jìn)入頁(yè)面后再修正)抵赢,具體操作可參照Demo:

// 1.【視頻】以NSURL傳入
JPImageresizerConfigure *configure = [JPImageresizerConfigure defaultConfigureWithVideoURL:videoURL make:^(JPImageresizerConfigure *configure) { ...... } fixErrorBlock:^(NSURL *cacheURL, JPImageresizerErrorReason reason) {
    // 初始化修正視頻方向的錯(cuò)誤回調(diào)
} fixStartBlock:^{
    // 初始化修正視頻方向的開(kāi)始回調(diào)
} fixProgressBlock:^(float progress) {
    // 初始化修正視頻方向的進(jìn)度回調(diào) 
} fixCompleteBlock:^(NSURL *cacheURL) {
    // 初始化修正視頻方向的完成回調(diào)
}];

// 2.【視頻】以AVURLAsset傳入
[JPImageresizerConfigure defaultConfigureWithVideoAsset:videoAsset 
                                                   make:^(JPImageresizerConfigure *configure) { ...... } 
                                          fixErrorBlock:^(NSURL *cacheURL, JPImageresizerErrorReason reason) { ...... } 
                                          fixStartBlock:^{ ...... } fixProgressBlock:^(float progress) { ...... } 
                                       fixCompleteBlock:^(NSURL *cacheURL) { ...... }];

又或者先修正再初始化(先修正后再進(jìn)入頁(yè)面)欺劳,可以使用JPImageresizerTool的API來(lái)修正,具體操作可參照Demo:

// 獲取視頻信息
AVURLAsset *videoAsset = [AVURLAsset assetWithURL:videoURL];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[videoAsset loadValuesAsynchronouslyForKeys:@[@"duration", @"tracks"] completionHandler:^{
    dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

AVAssetTrack *videoTrack = [videoAsset tracksWithMediaType:AVMediaTypeVideo].firstObject;
if (CGAffineTransformEqualToTransform(videoTrack.preferredTransform, CGAffineTransformIdentity)) {
    // 無(wú)需修正铅鲤,進(jìn)入裁剪界面
    JPImageresizerConfigure *configure = [JPImageresizerConfigure defaultConfigureWithVideoAsset:videoAsset make:nil fixErrorBlock:nil fixStartBlock:nil fixProgressBlock:nil fixCompleteBlock:nil];
    ......
    return;
}

// 修正方向
[JPImageresizerTool fixOrientationVideoWithAsset:videoAsset fixErrorBlock:^(NSURL *cacheURL, JPImageresizerErrorReason reason) {
    // 修正視頻方向的錯(cuò)誤回調(diào)
} fixStartBlock:^(AVAssetExportSession *exportSession) {
    // 修正視頻方向的開(kāi)始回調(diào)
    // 返回exportSession划提,可監(jiān)聽(tīng)進(jìn)度或取消導(dǎo)出
} fixCompleteBlock:^(NSURL *cacheURL) {
    // 修正視頻方向的完成回調(diào)
    // cacheURL:修正方向后的視頻導(dǎo)出后的最終存放路徑,默認(rèn)該路徑為NSTemporaryDirectory文件夾下邢享,保存該路徑鹏往,裁剪后刪除視頻。
    
    // 開(kāi)始裁剪骇塘,進(jìn)入裁剪界面
    JPImageresizerConfigure *configure = [JPImageresizerConfigure defaultConfigureWithVideoAsset:[AVURLAsset assetWithURL:cacheURL] make:nil fixErrorBlock:nil fixStartBlock:nil fixProgressBlock:nil fixCompleteBlock:nil];
    ......
}];
  • PS1:如果視頻不需要修正掸犬,fixStartBlock袜漩、fixProgressBlockfixErrorBlock均不會(huì)調(diào)用湾碎,會(huì)直接調(diào)用fixCompleteBlock蜻拨,返回原路徑辕近;
  • PS2:如果確定是無(wú)需修正方向的視頻,fixErrorBlock曲聂、fixStartBlock递惋、fixProgressBlock柔滔、fixCompleteBlocknil
  • PS3:更換視頻:-setVideoURL: animated: fixErrorBlock: fixStartBlock: fixProgressBlock: fixCompleteBlock:-setVideoAsset: animated: fixErrorBlock: fixStartBlock: fixProgressBlock: fixCompleteBlock: 方法也與之同理萍虽,內(nèi)部會(huì)判定是否需要修正睛廊;
  • PS4:如果需要初始化就固定裁剪寬高比(如圓切、蒙版等)杉编,需要設(shè)置JPImageresizerConfigureisArbitrarily屬性為NO(默認(rèn)為YES):
JPImageresizerConfigure *configure = [JPImageresizerConfigure darkBlurMaskTypeConfigureWithImage:nil make:^(JPImageresizerConfigure *configure) {
    configure
    .jp_maskImage([UIImage imageNamed:@"love.png"])
    .jp_isArbitrarily(NO);
}];

2. 創(chuàng)建JPImageresizerView對(duì)象并添加到視圖上

JPImageresizerView *imageresizerView = [JPImageresizerView imageresizerViewWithConfigure:configure imageresizerIsCanRecovery:^(BOOL isCanRecovery) {
    // 可在這里監(jiān)聽(tīng)到是否可以重置
    // 如果不需要重置(isCanRecovery為NO)超全,可在這里做相應(yīng)處理,例如將重置按鈕設(shè)置為不可點(diǎn)或隱藏
    // PS:isCanRecovery僅針對(duì)[旋轉(zhuǎn)]邓馒、[縮放]嘶朱、[鏡像]的變化情況,其他如裁剪寬高比光酣、圓切等變化情況需用戶(hù)自行判定能否重置
    // 具體操作可參照Demo
    // 注意循環(huán)引用
} imageresizerIsPrepareToScale:^(BOOL isPrepareToScale) {
    // 可在這里監(jiān)聽(tīng)到裁剪區(qū)域是否預(yù)備縮放至適合范圍
    // 如果預(yù)備縮放(isPrepareToScale為YES)疏遏,此時(shí)裁剪、旋轉(zhuǎn)救军、鏡像功能不可用财异,可在這里做相應(yīng)處理,例如將對(duì)應(yīng)按鈕設(shè)置為不可點(diǎn)或隱藏
    // 具體操作可參照Demo
    // 注意循環(huán)引用
}];
[self.view addSubview:imageresizerView];
self.imageresizerView = imageresizerView;

// 創(chuàng)建后均可動(dòng)態(tài)修改configure的參數(shù)
self.imageresizerView.image = [UIImage imageNamed:@"Kobe.jpg"]; // 更換裁剪圖片(默認(rèn)帶動(dòng)畫(huà)效果)
self.imageresizerView.resizeWHScale = 16.0 / 9.0; // 修改裁剪寬高比
self.imageresizerView.initialResizeWHScale = 0.0; // 默認(rèn)為初始化時(shí)的resizeWHScale唱遭,調(diào)用 -recoveryByInitialResizeWHScale 方法進(jìn)行重置則 resizeWHScale 會(huì)重置為該屬性的值

// 注意:iOS11以下的系統(tǒng)戳寸,所在的controller最好設(shè)置automaticallyAdjustsScrollViewInsets為NO
// 不然imageresizerView會(huì)隨導(dǎo)航欄或狀態(tài)欄的變化產(chǎn)生偏移
if (@available(iOS 11.0, *)) {

} else {
    self.automaticallyAdjustsScrollViewInsets = NO;
}

在Swift中的使用

// 1.初始配置
let configure = JPImageresizerConfigure.defaultConfigure(with: image) { c in
    _ = c
        .jp_viewFrame(frame)
        .jp_bgColor(.black)
        .jp_frameType(.classicFrameType)
        .jp_contentInsets(.init(top: 16, left: 16, bottom: 16, right: 16))
        .jp_animationCurve(.easeInOut)
}

// 2.創(chuàng)建imageresizerView
let imageresizerView = JPImageresizerView(configure: configure) { [weak self] isCanRecovery in
    // 當(dāng)不需要重置設(shè)置按鈕不可點(diǎn)
    self?.recoveryBtn.isEnabled = isCanRecovery
} imageresizerIsPrepareToScale: { [weak self] isPrepareToScale in
    // 當(dāng)預(yù)備縮放設(shè)置按鈕不可點(diǎn),結(jié)束后可點(diǎn)擊
    self?.operationView.isUserInteractionEnabled = !isPrepareToScale
}

// 3.添加到視圖上
view.insertSubview(imageresizerView, at: 0)
self.imageresizerView = imageresizerView

具體使用可以去Github下載Demo查看(JPCropViewController)胆萧。

裁剪

裁剪說(shuō)明:
    1.裁剪過(guò)程是在子線(xiàn)程中執(zhí)行庆揩,進(jìn)度、錯(cuò)誤跌穗、完成的回調(diào)都會(huì)切回主線(xiàn)程執(zhí)行订晌,如果是高清圖片,裁剪前可添加HUD提示蚌吸;
    2.compressScale:圖片和GIF的壓縮比例锈拨,大于等于1按原圖尺寸裁剪,小于等于0則返回nil(例:compressScale = 0.5羹唠,1000 x 500 --> 500 x 250)奕枢;
    3.cacheURL:緩存路徑娄昆,可設(shè)置為nil,圖片和GIF則不緩存缝彬,而視頻會(huì)默認(rèn)緩存到系統(tǒng)的NSTemporaryDirectory文件夾下萌焰,視頻名為當(dāng)前時(shí)間戳,格式為mp4谷浅;
    4.錯(cuò)誤原因 JPCropErrorReason 說(shuō)明:
        - JPCEReason_NilObject:裁剪元素為空
        - JPCEReason_CacheURLAlreadyExists:緩存路徑已存在其他文件
        - JPCEReason_NoSupportedFileType:不支持的文件類(lèi)型
        - JPCEReason_VideoAlreadyDamage:視頻文件已損壞
        - JPCEReason_VideoExportFailed:視頻導(dǎo)出失敗
        - JPCEReason_VideoExportCancelled:視頻導(dǎo)出取消
    5.注意:緩存路徑的圖片格式會(huì)自動(dòng)修正扒俯,例如原本寫(xiě)的是`xxx/xxx.jpeg`,由于使用了蒙版一疯,裁剪后則會(huì)修正為`xxx/xxx.png`了撼玄,最終的緩存路徑要以裁剪回調(diào)`completeBlock`中的`result.cacheURL`為準(zhǔn)。

裁剪圖片

// 1.以原圖尺寸進(jìn)行裁剪
[self.imageresizerView cropPictureWithCacheURL:cacheURL errorBlock:^(NSURL *cacheURL, JPImageresizerErrorReason reason) {
    // 錯(cuò)誤的回調(diào)
    // reason:錯(cuò)誤原因
    // 注意循環(huán)引用
} completeBlock:^(JPImageresizerResult *result) {
    // 裁剪完成
    // result:裁剪后的結(jié)果(JPImageresizerResult)
    // result.image:裁剪后的圖片(已解碼好的)
    // result.cacheURL:目標(biāo)存放路徑
    // result.isCacheSuccess:是否緩存成功(緩存不成功則cacheURL為nil)
    // 注意循環(huán)引用
}];


// 2.自定義壓縮比例裁剪圖片
// compressScale --- 壓縮比例墩邀,大于等于1按原圖尺寸裁剪掌猛,小于等于0則返回nil(例:compressScale = 0.5,1000 x 500 --> 500 x 250)
// completeBlock --- 裁剪完成的回調(diào)(返回裁剪后的結(jié)果眉睹,包含已解碼好的圖片荔茬、緩存路徑)
- (void)cropPictureWithCompressScale:(CGFloat)compressScale
                            cacheURL:(NSURL *)cacheURL
                          errorBlock:(JPImageresizerErrorBlock)errorBlock
                       completeBlock:(JPCropDoneBlock)completeBlock;
  • 裁剪N宮格圖片
image
// 1.自定義N宮格裁剪
// columnCount:N宮格的列數(shù)(最小1列)
// rowCount:N宮格的行數(shù)(最小1行)
// compressScale --- 壓縮比例,大于等于1按原圖尺寸裁剪辣往,小于等于0則返回nil(例:compressScale = 0.5兔院,1000 x 500 --> 500 x 250)
// bgColor --- N宮格的背景色(如果圖片有透明區(qū)域,或者設(shè)置了蒙版的情況才生效站削,設(shè)置隱藏(透明)區(qū)域的背景色)
[self.imageresizerView cropGirdPicturesWithColumnCount:4 rowCount:2 compressScale:1 bgColor:UIColor.redColor cacheURL:cacheURL errorBlock:^(NSURL *cacheURL, JPImageresizerErrorReason reason) {
    // 錯(cuò)誤的回調(diào)
    // reason:錯(cuò)誤原因
    // 注意循環(huán)引用
} completeBlock:^(JPImageresizerResult *originResult, NSArray<JPImageresizerResult *> *fragmentResults, NSInteger columnCount, NSInteger rowCount) {
    // 裁剪完成
    // originResult:裁剪后的原圖結(jié)果(開(kāi)始N宮格之前)
    // fragmentResults:裁剪后的原圖被裁剪成N宮格圖片的結(jié)果集合(共 columnCount * rowCount 個(gè))
    // columnCount:調(diào)用該方法時(shí)傳入的列數(shù)
    // rowCount:調(diào)用該方法時(shí)傳入的行數(shù)
    // 注意循環(huán)引用
}];

// 2.九宮格裁剪(3行3列)
- (void)cropNineGirdPicturesWithCompressScale:(CGFloat)compressScale
                                      bgColor:(UIColor *)bgColor
                                     cacheURL:(NSURL *)cacheURL
                                   errorBlock:(JPImageresizerErrorBlock)errorBlock
                                completeBlock:(JPCropNGirdDoneBlock)completeBlock;

分享至朋友圈(建議使用resizeWHScale = 1進(jìn)行裁剪):

image

裁剪GIF

image
image
// 1.原圖尺寸裁剪GIF
[self.imageresizerView cropGIFWithCacheURL:cacheURL errorBlock:^(NSURL *cacheURL, JPImageresizerErrorReason reason) {
    // 錯(cuò)誤的回調(diào)
    // reason:錯(cuò)誤原因
    // 注意循環(huán)引用
} completeBlock:^(JPImageresizerResult *result) {
    // 裁剪完成
    // result:裁剪后的結(jié)果(JPImageresizerResult)
    // result.image:裁剪后的GIF(已解碼好的)
    // result.cacheURL:目標(biāo)存放路徑
    // result.isCacheSuccess:是否緩存成功(緩存不成功則cacheURL為nil)
    // 注意循環(huán)引用
}];

// 2.自定義壓縮比例裁剪GIF
// completeBlock --- 裁剪完成的回調(diào)(返回裁剪后的結(jié)果坊萝,包含已解碼好的GIF、緩存路徑)
- (void)cropGIFWithCompressScale:(CGFloat)compressScale
                        cacheURL:(NSURL *)cacheURL
                      errorBlock:(JPImageresizerErrorBlock)errorBlock
                   completeBlock:(JPCropDoneBlock)completeBlock;

// 3.自定義裁剪GIF
// isReverseOrder --- 是否倒放
// rate --- 速率
// completeBlock --- 裁剪完成的回調(diào)(返回裁剪后的結(jié)果许起,包含已解碼好的GIF十偶、緩存路徑)
- (void)cropGIFWithCompressScale:(CGFloat)compressScale
                  isReverseOrder:(BOOL)isReverseOrder
                            rate:(float)rate
                        cacheURL:(NSURL *)cacheURL
                      errorBlock:(JPImageresizerErrorBlock)errorBlock
                   completeBlock:(JPCropDoneBlock)completeBlock;

裁剪GIF的其中一幀

// 1.原圖尺寸裁剪GIF當(dāng)前幀畫(huà)面
// completeBlock --- 裁剪完成的回調(diào)(返回裁剪后的結(jié)果,包含已解碼好的圖片园细、緩存路徑)
- (void)cropGIFCurrentIndexWithCacheURL:(NSURL *)cacheURL
                             errorBlock:(JPImageresizerErrorBlock)errorBlock
                          completeBlock:(JPCropDoneBlock)completeBlock;

// 2.自定義壓縮比例裁剪GIF當(dāng)前幀畫(huà)面
// completeBlock --- 裁剪完成的回調(diào)(返回裁剪后的結(jié)果惦积,包含已解碼好的圖片、緩存路徑)
- (void)cropGIFCurrentIndexWithCompressScale:(CGFloat)compressScale
                                    cacheURL:(NSURL *)cacheURL
                                  errorBlock:(JPImageresizerErrorBlock)errorBlock
                               completeBlock:(JPCropDoneBlock)completeBlock;

// 3.自定義壓縮比例裁剪GIF指定幀畫(huà)面
// index --- 第幾幀畫(huà)面
// compressScale --- 壓縮比例猛频,大于等于1按原圖尺寸裁剪狮崩,小于等于0則返回nil(例:compressScale = 0.5,1000 x 500 --> 500 x 250)
- (void)cropGIFWithIndex:(NSUInteger)index
           compressScale:(CGFloat)compressScale
                cacheURL:(NSURL *)cacheURL
              errorBlock:(JPImageresizerErrorBlock)errorBlock
           completeBlock:(JPCropDoneBlock)completeBlock;
  • PS:可以設(shè)置isLoopPlaybackGIF自主選擇裁剪哪一幀(默認(rèn)為NO鹿寻,設(shè)置為YES會(huì)自動(dòng)播放GIF)
image
self.imageresizerView.isLoopPlaybackGIF = NO;

裁剪本地視頻

裁剪本地視頻
  • PS:目前只針對(duì)本地視頻睦柴,遠(yuǎn)程視頻暫未適配。
// 裁剪整段視頻
// cacheURL:如果為nil毡熏,會(huì)默認(rèn)緩存到系統(tǒng)的NSTemporaryDirectory文件夾下坦敌,視頻名為當(dāng)前時(shí)間戳,格式為mp4
[self.imageresizerView cropVideoWithCacheURL:cacheURL errorBlock:^(NSURL *cacheURL, JPCropErrorReason reason) {
    // 錯(cuò)誤的回調(diào)
    // reason:錯(cuò)誤原因
    // 注意循環(huán)引用
} progressBlock:^(float progress) {
    // 監(jiān)聽(tīng)進(jìn)度
    // progress:0~1
    // 注意循環(huán)引用
} completeBlock:^(JPImageresizerResult *result) {
    // 裁剪完成
    // result:裁剪后的結(jié)果(JPImageresizerResult)
    // result.cacheURL:目標(biāo)存放路徑,如果為nil狱窘,會(huì)默認(rèn)緩存到系統(tǒng)的NSTemporaryDirectory文件夾下杜顺,視頻名為當(dāng)前時(shí)間戳,格式為mp4
    // result.isCacheSuccess:是否緩存成功(緩存不成功則cacheURL為nil)
    // 注意循環(huán)引用
}];

// 可設(shè)置視頻導(dǎo)出質(zhì)量
// presetName --- 系統(tǒng)的視頻導(dǎo)出質(zhì)量蘸炸,如:AVAssetExportPresetLowQuality躬络,AVAssetExportPresetMediumQuality,AVAssetExportPresetHighestQuality等
- (void)cropVideoWithPresetName:(NSString *)presetName
                       cacheURL:(NSURL *)cacheURL 
                     errorBlock:(JPCropErrorBlock)errorBlock
                  progressBlock:(JPExportVideoProgressBlock)progressBlock
                  completeBlock:(JPCropDoneBlock)completeBlock;

// 取消視頻導(dǎo)出
// 當(dāng)視頻正在導(dǎo)出時(shí)調(diào)用即可取消導(dǎo)出搭儒,觸發(fā)errorBlock回調(diào)(JPCEReason_ExportCancelled)
- (void)videoCancelExport;
  • PS:由于視頻的寬高都必須是16的整數(shù)倍洗鸵,否則導(dǎo)出后系統(tǒng)會(huì)自動(dòng)對(duì)尺寸進(jìn)行校正,不足的地方會(huì)以綠邊的形式進(jìn)行填充仗嗦,因此我在方法內(nèi)部對(duì)裁剪尺寸做了對(duì)16除余的修改,所以最后導(dǎo)出視頻的寬高比有可能跟指定的寬高比有些許差異甘凭。

裁剪視頻的其中一幀

// 1.原圖尺寸裁剪視頻當(dāng)前幀畫(huà)面
// cacheURL --- 緩存路徑(可設(shè)置為nil稀拐,則不會(huì)緩存)
// completeBlock --- 裁剪完成的回調(diào)(返回裁剪后的結(jié)果,包含已解碼好的圖片丹弱、緩存路徑)
- (void)cropVideoCurrentFrameWithCacheURL:(NSURL *)cacheURL
                               errorBlock:(JPImageresizerErrorBlock)errorBlock
                            completeBlock:(JPCropDoneBlock)completeBlock;

// 2.自定義壓縮比例裁剪視頻當(dāng)前幀畫(huà)面
// cacheURL --- 緩存路徑(可設(shè)置為nil德撬,則不會(huì)緩存)
// completeBlock --- 裁剪完成的回調(diào)(返回裁剪后的結(jié)果,包含已解碼好的圖片躲胳、緩存路徑)
- (void)cropVideoCurrentFrameWithCompressScale:(CGFloat)compressScale
                                      cacheURL:(NSURL *)cacheURL
                                    errorBlock:(JPImageresizerErrorBlock)errorBlock
                                 completeBlock:(JPCropDoneBlock)completeBlock;

// 3.自定義壓縮比例裁剪視頻指定幀畫(huà)面
// second --- 第幾秒畫(huà)面
// cacheURL --- 緩存路徑(可設(shè)置為nil蜓洪,則不會(huì)緩存)
// completeBlock --- 裁剪完成的回調(diào)(返回裁剪后的結(jié)果,包含已解碼好的圖片坯苹、緩存路徑)
- (void)cropVideoOneFrameWithSecond:(float)second
                      compressScale:(CGFloat)compressScale
                           cacheURL:(NSURL *)cacheURL
                         errorBlock:(JPImageresizerErrorBlock)errorBlock
                      completeBlock:(JPCropDoneBlock)completeBlock;

截取視頻某一段裁剪后轉(zhuǎn)GIF

截取視頻轉(zhuǎn)GIF
// 1.視頻從當(dāng)前時(shí)間開(kāi)始截取指定秒數(shù)畫(huà)面轉(zhuǎn)GIF(fps = 10隆檀,rate = 1,maximumSize = 500 * 500)
// duration --- 截取多少秒
// completeBlock --- 裁剪完成的回調(diào)(返回裁剪后的結(jié)果粹湃,包含已解碼好的GIF恐仑、緩存路徑)
- (void)cropVideoToGIFFromCurrentSecondWithDuration:(NSTimeInterval)duration
                                           cacheURL:(NSURL *)cacheURL
                                         errorBlock:(JPImageresizerErrorBlock)errorBlock
                                      completeBlock:(JPCropDoneBlock)completeBlock;

// 2.視頻自定義截取指定秒數(shù)畫(huà)面轉(zhuǎn)GIF
// duration --- 截取多少秒
// fps --- 幀率(設(shè)置為0則以視頻真身幀率)
// rate --- 速率
// maximumSize --- 截取的尺寸(設(shè)置為0則以視頻真身尺寸)
// completeBlock --- 裁剪完成的回調(diào)(返回裁剪后的結(jié)果,包含已解碼好的GIF为鳄、緩存路徑)
- (void)cropVideoToGIFFromStartSecond:(NSTimeInterval)startSecond
                             duration:(NSTimeInterval)duration
                                  fps:(float)fps
                                 rate:(float)rate
                          maximumSize:(CGSize)maximumSize
                             cacheURL:(NSURL *)cacheURL
                           errorBlock:(JPImageresizerErrorBlock)errorBlock
                        completeBlock:(JPCropDoneBlock)completeBlock;
  • PS:裁剪整段視頻畫(huà)面圓切裳仆、蒙版的功能不能使用,目前只對(duì)圖片和GIF有效孤钦。

蒙版

mask
// 設(shè)置蒙版圖片(目前僅支持png圖片)
self.imageresizerView.maskImage = [UIImage imageNamed:@"love.png"];

// 直接設(shè)置該值即是調(diào)用 -setMaskImage: isToBeArbitrarily: animated: 方法歧斟,其中默認(rèn) isToBeArbitrarily = (maskImage ? NO : self.isArbitrarily),isAnimated = YES

// 移除蒙版圖片
self.imageresizerView.maskImage = nil;
maskdone
  • PS:如果使用了蒙版圖片偏形,那么最后裁剪出來(lái)的是png圖片静袖,因此裁剪后體積有可能會(huì)比原本的圖片更大。


    吳彥祖換臉

圓切

圓切
// 設(shè)置圓切
// 設(shè)置后壳猜,resizeWHScale為1:1勾徽,半徑為寬高的一半,邊框的上、左喘帚、下畅姊、右的中部均可拖動(dòng)。
self.imageresizerView.isRoundResize = YES;

// 直接設(shè)置該值即是調(diào)用 -setIsRoundResize: isToBeArbitrarily: animated: 方法吹由,其中默認(rèn) isToBeArbitrarily = (isRoundResize ? NO : self.isArbitrarily)若未,isAnimated = YES

// 還原矩形
self.imageresizerView.isRoundResize = NO;
// 或者只需設(shè)置一下resizeWHScale為任意值即可
self.imageresizerView.resizeWHScale = 0.0;

橫豎屏切換

screenswitching
// 需要用戶(hù)去監(jiān)聽(tīng)橫豎屏的切換,或自己手動(dòng)切換時(shí)倾鲫,調(diào)用該方法刷新
// 1.updateFrame:刷新的Frame(例如橫豎屏切換粗合,傳入self.view.bounds即可)
// 2.contentInsets:裁剪區(qū)域與主視圖的內(nèi)邊距
// 3.duration:刷新時(shí)長(zhǎng)(大于0即帶有動(dòng)畫(huà)效果)
//【具體操作可參照Demo】
[self.imageresizerView updateFrame:self.view.bounds contentInsets:contentInsets duration:duration];

更改邊框樣式

簡(jiǎn)潔風(fēng)格
經(jīng)典風(fēng)格
// 目前只提供兩種邊框樣式,分別是簡(jiǎn)潔樣式JPConciseFrameType乌昔,和經(jīng)典樣式JPClassicFrameType
// 可在初始化或直接設(shè)置frameType屬性來(lái)修改邊框樣式
self.imageresizerView.frameType = JPClassicFrameType;

自定義邊框圖片

拉伸模式
平鋪模式
// 使用自定義邊框圖片(例:平鋪模式)
UIImage *tileBorderImage = [[UIImage imageNamed:@"jp_dotted_line"] resizableImageWithCapInsets:UIEdgeInsetsMake(14, 14, 14, 14) resizingMode:UIImageResizingModeTile];

// 設(shè)置邊框圖片與邊線(xiàn)的偏移量(即CGRectInset隙疚,用于調(diào)整邊框圖片與邊線(xiàn)的差距)
self.imageresizerView.borderImageRectInset = CGPointMake(-1.75, -1.75);

// 設(shè)置邊框圖片(若為nil則使用frameType的邊框)
self.imageresizerView.borderImage = tileBorderImage;

切換裁剪寬高比

  • PS:設(shè)置裁剪寬高比會(huì)自動(dòng)移除圓切和蒙版
// 1.自定義參數(shù)切換
/**
 * resizeWHScale:    目標(biāo)裁剪寬高比(0則為任意比例)
 * isToBeArbitrarily:切換之后 resizeWHScale 是否為任意比例(若為YES,最后 resizeWHScale = 0)
 * animated:         是否帶動(dòng)畫(huà)效果
 */
[self.imageresizerView setResizeWHScale:(16.0 / 9.0) isToBeArbitrarily:YES animated:YES];

// 2.直接切換
self.imageresizerView.resizeWHScale = 1.0;
// 默認(rèn)切換之后保存最新的 resizeWHScale磕道,且自帶動(dòng)畫(huà)效果供屉,如果設(shè)置為0,以當(dāng)前裁剪框的寬高比設(shè)置溺蕉,并且最后 isArbitrarily = YES伶丐,相當(dāng)于:
[self.imageresizerView setResizeWHScale:1.0 isToBeArbitrarily:(resizeWHScale <= 0) animated:YES];

// 是否可以任意比例拖拽(包括圓切、蒙版)
self.imageresizerView.isArbitrarily = !self.imageresizerView.isArbitrarily;

// 更多API可查看 JPImageresizerView.h 上的注釋

自定義毛玻璃樣式疯特、邊框顏色哗魂、背景顏色、遮罩透明度

// 設(shè)置毛玻璃樣式(默認(rèn)帶動(dòng)畫(huà)效果)
self.imageresizerView.blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];

// 設(shè)置邊框顏色(默認(rèn)帶動(dòng)畫(huà)效果)
self.imageresizerView.strokeColor = UIColor.whiteColor;

// 設(shè)置背景顏色(默認(rèn)帶動(dòng)畫(huà)效果)
self.imageresizerView.bgColor = UIColor.blackColor;

// 設(shè)置遮罩透明度(默認(rèn)帶動(dòng)畫(huà)效果)
// PS:跟毛玻璃互斥漓雅,當(dāng)設(shè)置了毛玻璃則遮罩為透明
self.imageresizerView.maskAlpha = 0.5; // blurEffect = nil 才生效

// 一步設(shè)置毛玻璃樣式录别、邊框顏色、背景顏色故硅、遮罩透明度
[self.imageresizerView setupStrokeColor:strokeColor blurEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark] bgColor:UIColor.blackColor maskAlpha: 0.5 animated:YES];

鏡像翻轉(zhuǎn)

效果圖.gif
// 垂直鏡像庶灿,YES->沿著Y軸旋轉(zhuǎn)180°,NO->還原
BOOL isVerticalityMirror = !self.imageresizerView.verticalityMirror;
[self.imageresizerView setVerticalityMirror:isVerticalityMirror animated:YES];

// 水平鏡像吃衅,YES->沿著X軸旋轉(zhuǎn)180°往踢,NO->還原
BOOL isHorizontalMirror = !self.imageresizerView.horizontalMirror;
[self.imageresizerView setHorizontalMirror:isHorizontalMirror animated:YES];

旋轉(zhuǎn)

// 1.順/逆時(shí)針旋轉(zhuǎn)90°(默認(rèn)逆時(shí)針)
[self.imageresizerView rotation];

// 若需要順時(shí)針旋轉(zhuǎn)可設(shè)置isClockwiseRotation屬性為YES
self.imageresizerView.isClockwiseRotation = YES;

// 2.自定義旋轉(zhuǎn)至目標(biāo)方向(支持4個(gè)方向,分別是垂直向上徘层、水平向左峻呕、垂直向下、水平向右)
[self.imageresizerView rotationToDirection:JPImageresizerVerticalDownDirection];

重置

重置目標(biāo)狀態(tài)趣效,方向垂直向上瘦癌,可重置為不同的resizeWHScale、圓切跷敬、蒙版

1. 一切按當(dāng)前狀態(tài)重置

- (void)recovery;

2. 以resizeWHScale重置(會(huì)移除圓切讯私、蒙版)

// 2.1 按初始裁剪寬高比(initialResizeWHScale)進(jìn)行重置
- (void)recoveryByInitialResizeWHScale;
- (void)recoveryByInitialResizeWHScale:(BOOL)isToBeArbitrarily;

// 2.2 按當(dāng)前裁剪寬高比進(jìn)行重置(如果resizeWHScale為0,則重置到整個(gè)裁剪元素區(qū)域)
- (void)recoveryByCurrentResizeWHScale;
- (void)recoveryByCurrentResizeWHScale:(BOOL)isToBeArbitrarily;

// 2.3 按目標(biāo)裁剪寬高比進(jìn)行重置(如果resizeWHScale為0,則重置到整個(gè)裁剪元素區(qū)域)
// targetResizeWHScale:目標(biāo)裁剪寬高比
// isToBeArbitrarily:重置之后 resizeWHScale 是否為任意比例(若為YES斤寇,最后 resizeWHScale = 0)
- (void)recoveryToTargetResizeWHScale:(CGFloat)targetResizeWHScale isToBeArbitrarily:(BOOL)isToBeArbitrarily;

3. 以圓切重置

- (void)recoveryToRoundResize;
- (void)recoveryToRoundResize:(BOOL)isToBeArbitrarily;

4. 以蒙版圖片重置

// 4.1 按當(dāng)前蒙版圖片重置
- (void)recoveryByCurrentMaskImage;
- (void)recoveryByCurrentMaskImage:(BOOL)isToBeArbitrarily;

// 4.2 指定蒙版圖片重置
- (void)recoveryToMaskImage:(UIImage *)maskImage isToBeArbitrarily:(BOOL)isToBeArbitrarily;

預(yù)覽

// 預(yù)覽模式:隱藏邊框桶癣,停止拖拽操作,用于預(yù)覽裁剪后的區(qū)域

// 1.默認(rèn)自帶動(dòng)畫(huà)效果
self.imageresizerView.isPreview = YES;

// 2.自定義是否帶動(dòng)畫(huà)效果
[self.imageresizerView setIsPreview:YES animated:NO]

保存當(dāng)前裁剪狀態(tài)

// 1.很Easy娘锁,直接調(diào)用saveCurrentConfigure方法獲取當(dāng)前裁剪的狀態(tài)牙寞,可用一個(gè)全局變量來(lái)保存該對(duì)象
JPImageresizerConfigure *savedConfigure = [self.imageresizerView saveCurrentConfigure];

// 2.重新打開(kāi)裁剪歷史
JPImageresizerView *imageresizerView = [JPImageresizerView imageresizerViewWithConfigure:savedConfigure imageresizerIsCanRecovery:^(BOOL isCanRecovery) {
    ......
} imageresizerIsPrepareToScale:^(BOOL isPrepareToScale) {
    ......
}];
[self.view addSubview:imageresizerView];
self.imageresizerView = imageresizerView;

// 3.可以設(shè)置JPImageresizerConfigure的isCleanHistoryAfterInitial屬性為YES,當(dāng)初始化結(jié)束后自動(dòng)清空歷史(默認(rèn)為YES)
// 或者直接調(diào)用cleanHistory方法清空歷史
save
  • PS1:若保存的savedConfigure.history.viewFrame跟當(dāng)前的viewFrame不一致莫秆,界面會(huì)導(dǎo)致錯(cuò)亂间雀,需要自行判斷是否一致才可重新打開(kāi);
  • PS2:另外目前只能在A(yíng)pp使用期間進(jìn)行保存镊屎,暫未實(shí)現(xiàn)持久化緩存惹挟。

其他

// 鎖定裁剪區(qū)域,鎖定后無(wú)法拖動(dòng)裁剪區(qū)域缝驳,NO則解鎖
self.imageresizerView.isLockResizeFrame = YES;

// 旋轉(zhuǎn)至水平方向時(shí)是否自適應(yīng)裁剪區(qū)域大小
// 當(dāng)圖片寬度比圖片高度小時(shí)匪煌,該屬性默認(rèn)YES,可手動(dòng)設(shè)為NO
self.imageresizerView.isAutoScale = NO;

安裝

JPImageresizerView 可通過(guò)CocoaPods安裝党巾,只需添加下面一行到你的podfile:

pod 'JPImageresizerView'

反饋地址

郵箱:zhoujianping24@hotmail.com
扣扣:184669029
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市霜医,隨后出現(xiàn)的幾起案子齿拂,更是在濱河造成了極大的恐慌,老刑警劉巖肴敛,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件署海,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡医男,警方通過(guò)查閱死者的電腦和手機(jī)砸狞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)镀梭,“玉大人刀森,你說(shuō)我怎么就攤上這事”ㄕ耍” “怎么了研底?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)透罢。 經(jīng)常有香客問(wèn)我榜晦,道長(zhǎng),這世上最難降的妖魔是什么羽圃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任乾胶,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘识窿。我一直安慰自己斩郎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布腕扶。 她就那樣靜靜地躺著孽拷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪半抱。 梳的紋絲不亂的頭發(fā)上脓恕,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音窿侈,去河邊找鬼炼幔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛史简,可吹牛的內(nèi)容都是我干的乃秀。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼圆兵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼跺讯!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起殉农,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤刀脏,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后超凳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體愈污,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年轮傍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了暂雹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡创夜,死狀恐怖杭跪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情驰吓,我是刑警寧澤揍魂,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站棚瘟,受9級(jí)特大地震影響现斋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜偎蘸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一庄蹋、第九天 我趴在偏房一處隱蔽的房頂上張望瞬内。 院中可真熱鬧,春花似錦限书、人聲如沸虫蝶。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)能真。三九已至,卻和暖如春扰柠,著一層夾襖步出監(jiān)牢的瞬間粉铐,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工卤档, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝙泼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓劝枣,卻偏偏與公主長(zhǎng)得像汤踏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舔腾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容