在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Android/ 介紹用來選擇輪廓圖像的一個類——Will J Miller
101 種讓你的網(wǎng)頁看起來更酷的方法
Android 項目是如何使用 Kotlin 語言生成的 1/2
Nimbledroid 版本更新
使用TypeScript提高開發(fā)能力
利用 TensorFlow Serving 系統(tǒng)在生產(chǎn)環(huán)境中運行模型
Google Play 榜單APP冷啟動速度分析報告
使用微信控制電腦(Python 版)
jQuery 3 中的新變動
RxAndroid 基礎(chǔ):第一部分
Swift in 2016
UI自動化測試基于Activity的封裝模式
2016年,有哪些適合你學(xué)習(xí)的編程語言?
Swift 響應(yīng)式編程
如何組合使用 VIM 編輯器與 IPYthon
如何使你的app更加流暢
介紹用來選擇輪廓圖像的一個類——Will J Miller
關(guān)于Android N 開發(fā)者預(yù)覽版的一些隨想

介紹用來選擇輪廓圖像的一個類——Will J Miller

譯者:李鑫

原文:A Class for Selecting a Profile Image

本文為極客學(xué)院Wiki組織翻譯,轉(zhuǎn)載請注明出處。

時間:2016.3.14

文章介紹如何使用 UIImagePickerController 重制通訊錄應(yīng)用中常見的輪廓圖像選擇功能。其中涉及到了很多問題。該文展示了一個類如何使用 UIImagePickerController 來復(fù)制這種功能。

簡介

如果想按照通訊錄應(yīng)用中那種功能來選擇并編輯圖像,似乎應(yīng)該采用 UIImagePickerController 類(圖像拾取器)。它似乎甚至還能完美地對編輯屏幕疊加層進(jìn)行自定義。但遺憾的是,事實證明它是不可改變的。復(fù)制這些功能讓我付出了常人難以想象的困難。

本文介紹了 MMSProfileImagePicker 類(輪廓圖像拾取器),它可以進(jìn)行圖像選擇和編輯,跟通訊錄應(yīng)用中的功能是等同的。

有些人已經(jīng)解決了這個問題,并把他們的類放到了開發(fā)者社區(qū)中進(jìn)行分享。所以你肯定會想,你的方案又有什么新意呢?很簡單,我只想通過這篇文章來展示一些技巧,你有可能會在解決其他難題時用上它。

借助于圖像拾取器,這種解決方案支持了這些功能。本文主要考慮與之集成的一些技術(shù),以及如何在你自己的應(yīng)用中使用 輪廓圖像拾取器。可下載到的范例文件中已經(jīng)實現(xiàn)了一個等同于通訊錄應(yīng)用中選擇圖像的功能。

范例應(yīng)用

圖 1 -范例應(yīng)用

探索過程

在探索如何使用圖像拾取器的疊加特性時,我遇到了一些令人困惑的問題,比如說,如何正確定位疊加并改變它的大???如何在用相機(jī)選擇圖像時,讓圓形只顯示在編輯屏幕呢?以及如何按照 z 順序正確定位圓形疊加層呢?當(dāng)然,這些只是我現(xiàn)在能想到的一部分問題而已。

有些問題當(dāng)時沒有解決方案,有些則很復(fù)雜,有些則跟類的內(nèi)部實現(xiàn)結(jié)合得過于緊密。比如說,stackoverflow 上的這個解法通過操縱圖像拾取器創(chuàng)建的私有視圖來顯示圓形疊加。

這種方法無疑是很聰明,但它容易受到將來 iOS 版本更新的影響。而且,它只適用于選擇一副圖片的情況,而不適用于拍照并編輯一副圖片的情況。換句話說就是:不可能在顯示相機(jī)時攔截導(dǎo)航委托調(diào)用,從而在移動并縮放屏幕(圖像編輯屏幕)出現(xiàn)之前插入疊加層。

要解決的問題太多,限于本文篇幅,就不贅述了。關(guān)于如何裁切位圖,請參考我之前的文章裁切圖像的 View 類。那里介紹到了這里所使用的 UIImage+Cropping 類別。

注意,我沒有用到任何開源解決方案,如有雷同,實屬巧合。

解決方法

我的策略就是,盡量發(fā)掘圖像拾取器的功能,因為它包含著大部分我所需要的功能。不幸的是,有些類的功能必須重新實現(xiàn)才能完美復(fù)制通訊錄應(yīng)用中的相關(guān)功能。其中的一個挑戰(zhàn)就是如何將圓形疊加層只顯示在編輯屏幕上。在配置為顯示相機(jī)時,疊加層會顯示在獲取圖像屏幕和編輯屏幕上。結(jié)果,解決這個問題時,會需要對之前圖像拾取器已經(jīng)解決過的問題進(jìn)行另一種考慮。

為了解決這種需求,我最終的結(jié)論是:最好是讓圖像拾取器負(fù)責(zé)呈現(xiàn)相機(jī)以及從相簿中選擇圖像這兩種屏幕,讓新的類負(fù)責(zé)創(chuàng)建并顯示圖像編輯屏幕。

這種方法還需要解決以下這些復(fù)雜問題:

  • 如何防止圖像拾取器呈現(xiàn)圖像編輯屏幕?
  • 如何從圖像拾取器過渡到編輯屏幕?
  • 如何從圖像拾取器抓取相機(jī)圖像然后轉(zhuǎn)換到編輯屏幕?

禁用圖像拾取器的編輯屏幕

這種解決方法徹底重建了編輯屏幕功能,而不采用圖像拾取器的編輯屏幕。當(dāng)配置為從相機(jī)中選擇圖像時,圖像拾取器會在獲取圖像和編輯屏幕上顯示 cameraOverlay 屬性。這一方法提供的解決方案可防止圓形疊加層顯示在相機(jī)的圖像獲取屏幕上。

要想在“從相簿中選擇”屏幕中顯示“自定義編輯”屏幕,所用的配置十分簡單。圖像拾取器的一個屬性可防止編輯屏幕的呈現(xiàn)。將 allowsEditing 屬性設(shè)為 NO,就會返回用戶的選擇,但不會顯示它。這就為顯示自定義編輯屏幕提供了一種非常簡單的方法。

遺憾的是,當(dāng)配置為相機(jī)選擇時,圖像拾取器并不按照這一屬性來做,依然顯示編輯屏幕。這下事情就變得棘手了。我可不想重寫一遍相機(jī)功能,但目前也沒有任何現(xiàn)成的方法來禁用它,而且說歸到底,我還是不想放棄這種方法。

如果我能找到一種方法讓圖像拾取器調(diào)用我的 action 方法(而不是它自己的),那么 輪廓圖像拾取器就會防止編輯屏幕呈現(xiàn)出來。鑒于那個圖像拾取器是一個導(dǎo)航控制器,那么通過支持 UINavigationControllerDelegate 接口,輪廓圖像拾取器能能知道它所要過渡到的視圖。但如果圖像拾取器通過應(yīng)用的視圖控制器中顯示,UIKit 會調(diào)用視圖控制器上的方法。

所以,不能讓應(yīng)用來處理導(dǎo)航委托。輪廓圖像拾取器創(chuàng)建一個代理視圖控制器,它的責(zé)任就是處理導(dǎo)航委托,將調(diào)用轉(zhuǎn)發(fā)給 輪廓圖像拾取器。

在導(dǎo)航委托方法 navigationController:didShowViewController:animated: 中,輪廓圖像拾取器搜索視圖層級,查找拍照按鈕。步驟如下:

檢查是否相機(jī)將要顯示視圖:

if (imagePicker.sourceType == UIImagePickerControllerSourceTypeCamera && !isSnapPhotoTargetAdded && isPresentingCamera)

在底部視圖欄的子視圖列表中的索引 8 處,找到了拍照按鈕視圖:

UIView* bottomBarView = [viewController.view.subviews objectAtIndex:2];
UIButton* buttonView = [bottomBarView.subviews objectAtIndex:8];

刪除處理 UIControlEventTouchUpInside 事件所用的 action 方法:

[buttonView removeTarget:viewController.view action:NULL forControlEvents:UIControlEventTouchUpInside];

為該按鈕添加自定義處理器:

[buttonView addTarget:self action:@selector(takePhoto:) forControlEvents:UIControlEventTouchUpInside];

如此一來,當(dāng)用戶點擊按鈕拍照時,輪廓圖像拾取器的 action 方法 takePhoto: 會被調(diào)用,圖像拾取器就無法顯示它的編輯屏幕了。

過渡到編輯屏幕

輪廓圖像拾取器是負(fù)責(zé)呈現(xiàn)自定義編輯屏幕的視圖控制器。它創(chuàng)建了視圖和一些子視圖,實現(xiàn)了圓形疊加層和圖像,并處理屏幕的按鈕事件。盡管布局這個屏幕的難度并不十分恐怖,但這其中仍然存在一些布局計算:顯示圖像并使之居中,使疊加層居中,以及創(chuàng)建滾動視圖的 contentInset

因為本文主要想介紹的是如何整合這一對象與圖像拾取器。所以關(guān)于細(xì)節(jié)方面的東西就留待讀者下載源碼去深入研究了。

過渡到編輯屏幕的方法有三種:

  • 應(yīng)用顯示一個已有圖像,用于編輯。
  • 從相簿中選擇一副圖像。
  • 相機(jī)拍照,抓取一副圖像。

顯示已有圖像

在這一用例下,應(yīng)用請求 輪廓圖像拾取器編輯已有圖像,利用 presentEditScreen:withImage: 方法所體現(xiàn)的模式顯示風(fēng)格來顯示編輯屏幕。

/* presentEditScreen: presents the move and scale window for a supplied image.  This use case is for when all that's required is to crop an image not to select one from the camera or photo album before cropping.
 */  

 /* 注釋解釋——presentEditScreen: 為使用的圖像顯示移動與縮放窗口。這一用例適用于萬事俱備,只等裁切圖像的情況,而不是還要先從相機(jī)或相簿中獲取圖像然后再裁切的情況。

 */

-(void)presentEditScreen:(UIViewController* _Nonnull)vc withImage:(UIImage* _Nonnull)image{ 

    isDisplayFromPicker =  isPresentingCamera = NO;

    imageToEdit = image;

    presentingVC = vc;

    self.modalPresentationStyle = UIModalPresentationFullScreen;

    [presentingVC presentViewController:self animated:YES completion:nil];

}

從相簿中選擇一副圖像

當(dāng)應(yīng)用請求 輪廓圖像拾取器顯示相簿選擇時,會將圖像拾取器的 sourceType 屬性設(shè)置為 UIImagePickerControllerSourceTypePhotoLibrary。輪廓圖像拾取器并不依賴圖像拾取器去顯示自己的編輯屏幕,所以會將圖像拾取器的 allowsEditing 屬性設(shè)為 NO。

當(dāng)用戶選擇了一副圖像時,圖像拾取器會調(diào)用輪廓圖像拾取器控制器上的委托方法 imagePickerController:didFinishPickingMediaWithInfo。輪廓圖像拾取器引用圖像,調(diào)用它的 editImage 方法來顯示帶有該圖像的編輯屏幕。

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {

    UIImage* tempImage = [info objectForKey:UIImagePickerControllerOriginalImage];

    [self editImage:tempImage];
}

因為圖像拾取器是一個導(dǎo)航控制器,所以它會將編輯屏幕推入堆棧,以便支持取消導(dǎo)航。

[imagePicker pushViewController:self animated:NO]

除非導(dǎo)航欄被隱藏,否則它將顯示在編輯屏幕上。在推動它之前,會設(shè)置屬性來隱藏該欄。

[imagePicker setNavigationBarHidden:YES]

editImage 完整實現(xiàn)如下所示:

/* editImage: 利用輸入圖像對移動并縮放視圖進(jìn)行初始化,并將視圖顯示出來。只有當(dāng)用戶獲取或選擇圖像后,才能從 `presentCamera` 和 `presentPhotoPicker` 中調(diào)用它。
 */

-(void)editImage:(UIImage*)image {    

    imageToEdit = image;

    self.modalPresentationStyle = UIModalPresentationFullScreen;

    [imagePicker setNavigationBarHidden:YES];

    [imagePicker pushViewController:self animated:NO];

}

從相機(jī)中選擇一副圖像

跟從相簿中選擇圖像相似,為了顯示相機(jī),將 sourceType 設(shè)為 UIImagePickerControllerSourceTypeCamera。當(dāng)拍照后,輪廓圖像拾取器就會調(diào)用 editImage 顯示編輯屏幕。

抓取相機(jī)圖像

為了防止圖像拾取器顯示編輯屏幕,輪廓圖像拾取器取代了圖像拾取器的 action 方法,但這樣一來也就無法。輪廓圖像拾取器應(yīng)該將設(shè)備中的圖像傳輸?shù)较鄼C(jī)視圖中。
Profile picker has responsibility for transferring the image in the camera’s view from the device.

如果你們對其中的算法感興趣的話,可以自己研究一下這些代碼,如果發(fā)現(xiàn)其中有問題,請不吝賜教。具體來說,可以看看 MMSProfileImagePicker.m 文件中的下列方法:

  • prepareToCaptureStillImage: 用來創(chuàng)建 AVCaptureSession 并對其進(jìn)行初始化。
  • captureStillImage: 負(fù)責(zé)將從相機(jī)視圖中的圖像到數(shù)據(jù)緩沖的傳輸過程進(jìn)行初始化。
  • cameraConnection: 找到相機(jī)的 AVCaptureConnection 并返回它。
  • getCameraDevice: 返回iPhone 背蓋后相機(jī)的 AVCaptureDevice。
  • captureInpute: 將設(shè)備的 AVCaptureDeviceInput 添加到會話中。

captureStillImage 方法獲取相機(jī)圖像,并利用收到的圖像調(diào)用 editImage。

在獲取圖像過程中出現(xiàn)了一個問題。在第一次實現(xiàn)時,圖像質(zhì)量很低:比起利用相機(jī)應(yīng)用獲取的圖像而言,它顯得很暗。

我上網(wǎng)查了查,有一個方法建議:在開啟 AVCaptureSession 后,要延遲調(diào)用 captureStillImageAsynchronouslyFromConnection。插入一個 0.75 秒的延遲時間就能解決這個問題。我覺得肯定是因為缺少什么設(shè)置才導(dǎo)致了這種問題。雖然插入一個原因尚不清楚的延遲能把問題解決,但這很不可靠。我暫時還沒有別的辦法,它也暫時還能用,但如果你們找到了其他的辦法,一定要給我留個言說一說。

使用 MMSProfileImagePicker

要想讓你的應(yīng)用模擬原生的通訊錄應(yīng)用上的某些功能,類就必須支持三個公共接口:

調(diào)用 presentEditScreen: 方法來編輯應(yīng)用中已獲得的圖像。

-(void)presentEditScreen:(UIViewController* _Nonnull)vc withImage:(UIImage* _Nonnull)image;

調(diào)用 selectFromPhotoLibrary: 方法從相簿中選擇圖像:

-(void)selectFromPhotoLibrary:(UIViewController* _Nonnull)vc;

調(diào)用 selectFromCamera: 方法通過拍照來選擇圖像。

-(void)selectFromCamera:(UIViewController* _Nonnull)vc;

為了接收并編輯圖像,應(yīng)用必須在顯示profile拾取器的視圖控制器上實現(xiàn) MMSProfileImagePickerDelegate(輪廓圖像拾取器委托)委托方法。在調(diào)用預(yù)期方法來選擇圖像前,先要設(shè)置 輪廓圖像拾取器的 delegate 屬性。

- (IBAction)moveAndScale{

    profilePicker = [[MMSProfileImagePicker alloc] init];

    profilePicker.delegate = self;

    [profilePicker presentEditScreen:self withImage:originalImage];

}

輪廓圖像拾取器委托是一個跟 UIImagePickerControllerDelegate (圖像拾取器委托)類似的接口,目的也比較相像:接收選擇并被編輯的照片。在退出該方法之前,應(yīng)用應(yīng)該先清除輪廓圖像拾取器。

-(void)mmsImagePickerController:(MMSProfileImagePicker *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {        

    cropImage = [info objectForKey:UIImagePickerControllerEditedImage];

    originalImage = [info objectForKey:UIImagePickerControllerOriginalImage];

    self.circleImageView.image = cropImage;

    self.squareImageView.image = cropImage;

    self.btnAdd.hidden = YES;

    self.circleImageView.hidden = NO;

    self.btnEdit.hidden = NO;

    [picker dismissViewControllerAnimated:YES completion:nil];

} 

字典參數(shù)支持與圖像拾取器委托相同的實現(xiàn)中所定義的所有編輯信息鍵。關(guān)于編輯信息鍵,請參看相關(guān)的iOS 開發(fā)者文檔

另一方面,用戶可能已經(jīng)選擇圖像,然后決定退出操作。輪廓圖像拾取器調(diào)用委托方法 mmsImagePickerControllerDidCancel:。應(yīng)用應(yīng)該調(diào)用 dismissModalViewControllerAnimated: 來解除 輪廓圖像拾取器。

-(void)mmsImagePickerControllerDidCancel:(MMSProfileImagePicker *)picker {

    [self dismissViewControllerAnimated:YES completion:nil];

}

總結(jié)

在軟件開發(fā)中,解決問題的最佳辦法幾乎是不存在的。的確,我們都有各自的強(qiáng)有力的觀點。然而,還是存在著好辦法、更好的辦法、不同的辦法、糟糕的辦法這些差別。如果你能看到應(yīng)用的核心源碼,你就會從中看到上述所有這些辦法的樣板。競爭約束、團(tuán)隊文化、開發(fā)經(jīng)驗以及個人風(fēng)格……這些因素都能影響我們的解決方案。

寫作本文的目的在于展示一個問題的不同解決方案。在開發(fā)一個應(yīng)用時,我力圖首先挖掘架構(gòu)本身的能力,寫最少的代碼,在創(chuàng)建可用的組件或工具時,注重業(yè)務(wù)邏輯的開發(fā),在可行的情況下使用第三方庫。

我原打算 Cocoa Touch 架構(gòu)可以為這個例子提供一些方案,但我發(fā)現(xiàn)它并不支持現(xiàn)有的功能。但是,它似乎可以擴(kuò)展疊加層支持功能。所以為了少寫代碼的目的,我還是研究了一下那個方法。

結(jié)果我發(fā)現(xiàn)這種方法很不完善,而且不靈活。由于為此花了不少時間,我覺得自己應(yīng)該能寫出自己的編輯屏幕,然后只要將它配置好即可。但由于沒有經(jīng)驗,還是一頭撞了南墻,我發(fā)現(xiàn)根本不可能那么簡單。當(dāng)我解決好一個問題之后,就像剝洋蔥似的,另一個新問題馬上蹦了出來,最終我找到本文所說的這種方法。

希望這篇文章能讓你知道這種問題的解決方法,以及一些在其他項目中可能會用到的一些技術(shù)。如果你非常喜歡這個功能,完全可以把它用到你自己的應(yīng)用中,那就太好不過了。當(dāng)然,如果能為這個小組件提出改善方案就更好了。

cocoapods.org上可以找到該類。找到 MMSProfileImagePicker 并使用它吧。如果你想找到代碼中的缺陷或加以改進(jìn),可以在 Github 倉庫上找到這些代碼。

祝你編程愉快!