本章節主要爲以前項目 JXHomepwner 添加照片功能(項目地址)。具體任務就是顯示一個 UIImagePickerController 對象,使用戶可以爲 JXItem 對象拍照並保存。拍攝的照片會和相應的 JXItem 對象創建關聯,當用戶進入某個 JXItem 對象的詳細視圖的時候,能夠看見以前拍攝的照片。git
照片的文件可能很大,最後與 JXItem 對象的其餘數據分開保存。咱們將創建一個用於存儲數據的類 JXImageStore ,負責保存 JXItem 對象的照片。JXImageStore 能夠按須要獲取並緩存照片,還能夠在設備內存太低的時候清空緩存中的照片。github
首先要將照片賦值給 JXDetailViewController 對象,才能在該對象的視圖中顯示。要在視圖中顯示照片信息,一個最簡單的方法就是 UIImageView 對象。在 XIB 中放置一個 UIImageView 控件。數組
UIImageView 對象會根據其 contentModel 屬性來顯示一張指定的圖片模式。 contentModel 屬性的做用是肯定圖片的 frame 內的顯示位置和縮放模式。其默認值是 UIViewContentModelScaleToFill 。當其屬性值是默認值時,UIImageView 對象會在顯示圖片時縮放圖片的大小,使其可以填滿整個視圖空間,可是可能會改變圖片的寬高比。緩存
#import "JXDetailViewController.h" #import "JXItem.h" @interface JXDetailViewController () @property (weak, nonatomic) IBOutlet UITextField *nameField; @property (weak, nonatomic) IBOutlet UITextField *seriaNumberField; @property (weak, nonatomic) IBOutlet UITextField *valueField; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation JXDetailViewController - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 取消當前的第一響應對象 [self.view endEditing:YES]; // 將修改保存到 JXItem JXItem * item = self.item; item.itemName = self.nameField.text; item.serialnumber = self.seriaNumberField.text; item.valueInDollars = [self.valueField.text integerValue]; } - (void)viewDidLoad { [super viewDidLoad]; JXItem * item = self.item; self.nameField.text = item.itemName; self.seriaNumberField.text = item.itemName; self.valueField.text = [NSString stringWithFormat:@"%ld",item.valueInDollars]; // 建立 NSDdateFoemateter 對象,用於將 NSDate 對象轉換成簡單的日期字符串 static NSDateFormatter * dateFormatter = nil; if (!dateFormatter) { dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterNoStyle; } // 將轉換後獲得的日期字符串設置爲 dateLabel 的標題 self.dateLabel.text = [dateFormatter stringFromDate:item.createDate]; } - (void)setItem:(JXItem *)item { _item = item; self.navigationItem.title = _item.itemName; } @end
添加相機按鈕安全
爲應用程序添加一個按鈕,用於在啓動拍照。爲此,咱們須要先建立一個 UIToolbar 對象,而後該對象放置在 JXDetailViewController 對象的視圖底部,最後將按鈕放置到 UIToolbar 對象上。服務器
UIToolbar 的工做方式和 UINavigationBar 很類似,一樣能夠加入 UIBarButtonItem 對象。區別就是 UINavigationBar 只能左右兩端放置按鈕,可是 UIToolbar 對象能夠有一組 UIBarButtonItem 對象。只要屏幕可以容納,UIToolbar 對象自身並無顯示能夠存放的 UIBarButtonItem 對象的個數。數據結構
創建關聯app
關聯以後代碼以下dom
#import "JXDetailViewController.h" #import "JXItem.h" @interface JXDetailViewController () @property (weak, nonatomic) IBOutlet UITextField *nameField; @property (weak, nonatomic) IBOutlet UITextField *seriaNumberField; @property (weak, nonatomic) IBOutlet UITextField *valueField; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation JXDetailViewController - (IBAction)takePicture:(id)sender { } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 取消當前的第一響應對象 [self.view endEditing:YES]; // 將修改保存到 JXItem JXItem * item = self.item; item.itemName = self.nameField.text; item.serialnumber = self.seriaNumberField.text; item.valueInDollars = [self.valueField.text integerValue]; } - (void)viewDidLoad { [super viewDidLoad]; JXItem * item = self.item; self.nameField.text = item.itemName; self.seriaNumberField.text = item.itemName; self.valueField.text = [NSString stringWithFormat:@"%ld",item.valueInDollars]; // 建立 NSDdateFoemateter 對象,用於將 NSDate 對象轉換成簡單的日期字符串 static NSDateFormatter * dateFormatter = nil; if (!dateFormatter) { dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterNoStyle; } // 將轉換後獲得的日期字符串設置爲 dateLabel 的標題 self.dateLabel.text = [dateFormatter stringFromDate:item.createDate]; } - (void)setItem:(JXItem *)item { _item = item; self.navigationItem.title = _item.itemName; } @end
接下來,咱們須要在 takePicture: 方法中建立並顯示 UIImagePickerController 對象。建立該對象時,必須爲新建立的對象設置 sourceType 屬性和 delegate 屬性。ide
設置 UIImagePickerController 對象的源
設置 sourceType 屬性時必須使用特性的常量,這些常量表示 UIImagePicker、Controller 對象獲取照片的源。目前咱們有三種可以使用的常量。
1. UIImagePickerControllerSourceTypeCamera :用於用戶拍攝一張新的圖片
2. UIImagePickerControllerSourceTypePhotoLibrary :用於顯示界面,讓用戶選擇相冊,而後從選中的相冊中選一張照片
3. UIImagePickerControllerSourceTypeSavephotosAlbum :用於讓用戶從最近拍攝的照片裏選擇一張照片。
對於沒有相機的設備(也就只有在模擬器上了),選取第一種類型是無效的,因此咱們在使用第一種 變量以前,應該先向 UIImagePickerController 類發送 isSourceTypeAvailable: 消息,檢查設備時候支持相機。發送該消息時,須要傳入待檢查的選取類型常量。
+ (BOOL)isSourceTypeAvailable:(UIImagePickerControllerSourceType)sourceType; // returns YES if source is available (i.e. camera present)
該方法會返回一個布爾值,用來斷定設備是否支持。
#import "JXDetailViewController.h" #import "JXItem.h" @interface JXDetailViewController () @property (weak, nonatomic) IBOutlet UITextField *nameField; @property (weak, nonatomic) IBOutlet UITextField *seriaNumberField; @property (weak, nonatomic) IBOutlet UITextField *valueField; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIToolbar *toolbar; @end @implementation JXDetailViewController - (IBAction)takePicture:(id)sender { UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init]; // 若是設備支持相機,就使用拍照模式 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; } else { imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; } } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 取消當前的第一響應對象 [self.view endEditing:YES]; // 將修改保存到 JXItem JXItem * item = self.item; item.itemName = self.nameField.text; item.serialnumber = self.seriaNumberField.text; item.valueInDollars = [self.valueField.text integerValue]; } - (void)viewDidLoad { [super viewDidLoad]; JXItem * item = self.item; self.nameField.text = item.itemName; self.seriaNumberField.text = item.itemName; self.valueField.text = [NSString stringWithFormat:@"%ld",item.valueInDollars]; // 建立 NSDdateFoemateter 對象,用於將 NSDate 對象轉換成簡單的日期字符串 static NSDateFormatter * dateFormatter = nil; if (!dateFormatter) { dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterNoStyle; } // 將轉換後獲得的日期字符串設置爲 dateLabel 的標題 self.dateLabel.text = [dateFormatter stringFromDate:item.createDate]; } - (void)setItem:(JXItem *)item { _item = item; self.navigationItem.title = _item.itemName; } @end
設置 UIImagePickerController 對象的委託
除了 sourceType 屬性以外,還須要爲 UIImagePickerController 對象設置委託。用戶從 UIImagePickerController 對象中選擇了一張圖片以後,委託會受到
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(nullable NSDictionary<NSString *,id> *)editingInfo NS_DEPRECATED_IOS(2_0, 3_0);(已經廢棄)
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
若是用戶取消選中,那麼會收到
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;
UIImagePickerController 對象的委託一般設置爲須要獲取照片的對象。所以,遵照協議 UINavigationControllerDelegate 和 UIImagePickerControllerDelegate 而後設置委託。
#import "JXDetailViewController.h" #import "JXItem.h" @interface JXDetailViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate> @property (weak, nonatomic) IBOutlet UITextField *nameField; @property (weak, nonatomic) IBOutlet UITextField *seriaNumberField; @property (weak, nonatomic) IBOutlet UITextField *valueField; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIToolbar *toolbar; @end @implementation JXDetailViewController - (IBAction)takePicture:(id)sender { UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init]; // 若是設備支持相機,就使用拍照模式 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; } else { imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; } imagePicker.delegate = self; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 取消當前的第一響應對象 [self.view endEditing:YES]; // 將修改保存到 JXItem JXItem * item = self.item; item.itemName = self.nameField.text; item.serialnumber = self.seriaNumberField.text; item.valueInDollars = [self.valueField.text integerValue]; } - (void)viewDidLoad { [super viewDidLoad]; JXItem * item = self.item; self.nameField.text = item.itemName; self.seriaNumberField.text = item.itemName; self.valueField.text = [NSString stringWithFormat:@"%ld",item.valueInDollars]; // 建立 NSDdateFoemateter 對象,用於將 NSDate 對象轉換成簡單的日期字符串 static NSDateFormatter * dateFormatter = nil; if (!dateFormatter) { dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterNoStyle; } // 將轉換後獲得的日期字符串設置爲 dateLabel 的標題 self.dateLabel.text = [dateFormatter stringFromDate:item.createDate]; } - (void)setItem:(JXItem *)item { _item = item; self.navigationItem.title = _item.itemName; } @end
以模態的形式顯示 UIImagePickerController 對象
爲 UIImagePickerController 對象設置了源類型和委託以後,就能夠在屏幕中顯示該對象。和以前的 UIViewController 子類對象不一樣,該對象必須以模態(modal)形式顯示。
要以模態形式顯示某個視圖控制器,須要向窗口當前顯示的 UIViewController 對象發送
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^ __nullable)(void))completion NS_AVAILABLE_IOS(5_0);
同時第一個參數爲須要顯示的視圖控制器,第二個參數設置時候有動畫效果,第三個參數爲顯示以後須要進行什麼操做。
#import "JXDetailViewController.h" #import "JXItem.h" @interface JXDetailViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate> @property (weak, nonatomic) IBOutlet UITextField *nameField; @property (weak, nonatomic) IBOutlet UITextField *seriaNumberField; @property (weak, nonatomic) IBOutlet UITextField *valueField; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIToolbar *toolbar; @end @implementation JXDetailViewController - (IBAction)takePicture:(id)sender { UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init]; // 若是設備支持相機,就使用拍照模式 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; } else { imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; } imagePicker.delegate = self; [self presentViewController:imagePicker animated:YES completion:nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 取消當前的第一響應對象 [self.view endEditing:YES]; // 將修改保存到 JXItem JXItem * item = self.item; item.itemName = self.nameField.text; item.serialnumber = self.seriaNumberField.text; item.valueInDollars = [self.valueField.text integerValue]; } - (void)viewDidLoad { [super viewDidLoad]; JXItem * item = self.item; self.nameField.text = item.itemName; self.seriaNumberField.text = item.itemName; self.valueField.text = [NSString stringWithFormat:@"%ld",item.valueInDollars]; // 建立 NSDdateFoemateter 對象,用於將 NSDate 對象轉換成簡單的日期字符串 static NSDateFormatter * dateFormatter = nil; if (!dateFormatter) { dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterNoStyle; } // 將轉換後獲得的日期字符串設置爲 dateLabel 的標題 self.dateLabel.text = [dateFormatter stringFromDate:item.createDate]; } - (void)setItem:(JXItem *)item { _item = item; self.navigationItem.title = _item.itemName; } @end
構建並運行,發現會crash,在iOS 10 上咱們須要設置一些權限。
相機權限: Privacy - Camera Usage Description 是否容許此App使用你的相機?
相冊權限: Privacy - Photo Library Usage Description 是否容許此App訪問你的媒體資料庫?
保存照片
選擇一張照片以後,UIImagePickerController 對象就會自動關閉,返回咱們以前界面,這時候咱們以前界面是不可能有任何數據的,由於咱們選擇照片以後沒有任何一句代碼是保存咱們選中的照片的。因此咱們須要經過上面介紹的代理方法來進行選中後的保存回調。
#import "JXDetailViewController.h" #import "JXItem.h" @interface JXDetailViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate> @property (weak, nonatomic) IBOutlet UITextField *nameField; @property (weak, nonatomic) IBOutlet UITextField *seriaNumberField; @property (weak, nonatomic) IBOutlet UITextField *valueField; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIToolbar *toolbar; @end @implementation JXDetailViewController - (IBAction)takePicture:(id)sender { UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init]; // 若是設備支持相機,就使用拍照模式 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; } else { imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; } imagePicker.delegate = self; [self presentViewController:imagePicker animated:YES completion:nil]; } - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info { // 經過 info 字典獲取選中的照片 UIImage * image = info[UIImagePickerControllerOriginalImage]; // 將照片放入 UIImageView 對象 self.imageView.image = image; // 關閉 UIImagePickerController 對象 [self dismissViewControllerAnimated:YES completion:nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 取消當前的第一響應對象 [self.view endEditing:YES]; // 將修改保存到 JXItem JXItem * item = self.item; item.itemName = self.nameField.text; item.serialnumber = self.seriaNumberField.text; item.valueInDollars = [self.valueField.text integerValue]; } - (void)viewDidLoad { [super viewDidLoad]; JXItem * item = self.item; self.nameField.text = item.itemName; self.seriaNumberField.text = item.itemName; self.valueField.text = [NSString stringWithFormat:@"%ld",item.valueInDollars]; // 建立 NSDdateFoemateter 對象,用於將 NSDate 對象轉換成簡單的日期字符串 static NSDateFormatter * dateFormatter = nil; if (!dateFormatter) { dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterNoStyle; } // 將轉換後獲得的日期字符串設置爲 dateLabel 的標題 self.dateLabel.text = [dateFormatter stringFromDate:item.createDate]; } - (void)setItem:(JXItem *)item { _item = item; self.navigationItem.title = _item.itemName; } @end
JXImageStore 對象將負責保存用戶所拍攝的全部照片。
#import <UIKit/UIKit.h> @interface JXImageStore : NSObject + (instancetype)sharedStore; /** * 保存圖片 * * @param image 圖片(字典中值) * @param key 圖片名稱(字典中鍵) */ - (void)setImage:(UIImage *)image forKey:(NSString *)key; /** * 取出圖片 * * @param key 圖片名稱 * * @return 取出的圖片 */ - (UIImage *)imageForKey:(NSString *)key; /** * 刪除圖片 * * @param key 根據key刪除圖片 */ - (void)deleteImageForKey:(NSString *)key; @end
#import "JXImageStore.h" @interface JXImageStore () /** 存儲照片 */ @property (nonatomic,strong) NSMutableDictionary * dictionary; @end @implementation JXImageStore + (instancetype)sharedStore { static JXImageStore * shareStore = nil; if (!shareStore) { shareStore = [[self alloc] init]; } return shareStore; } - (instancetype)init { self = [super init]; if (self) { self.dictionary = [NSMutableDictionary dictionary]; } return self; } - (void)setImage:(UIImage *)image forKey:(NSString *)key { self.dictionary[key] = image; } - (UIImage *)imageForKey:(NSString *)key { return self.dictionary[key]; } - (void)deleteImageForKey:(NSString *)key { if (!key) return; [self.dictionary removeObjectForKey:key]; } @end
JXImageStore 的屬性 dictionary 是一個指向 NSMutableDictionary 對象的指針。和數組對象相似,字典對象也是 Collection 對象,也有可修改和不可修改版本。
字典對象是由 鍵值對 組成的。這裏的鍵必定是可哈希的補課修改的對象,一般咱們使用NSString對象來作鍵。
字典很是有用,其中最經常使用的就是可變數據結構和查詢表。
對於可變數據結構。爲了在代碼中描述一個模型對象,常見的作法是建立一個 NSObject 的子類,而後添加模型對象的相關屬性。例如,對於一個表示 ‘人’ 的模型對象來講,能夠建立一個名爲 Person 的 NSObject 的子類,而後添加姓名,年齡和其餘全部須要的屬性。相似的, NSDictionary 也能夠用來描述模型對象。仍是以 ‘人’ 爲例, NSDictionary 中能夠針對姓名、年齡和其餘所須要的屬性保存響應的鍵值對。
使用 NSDictionary 和與咱們自定義的模型的區別就是,咱們自定義的模型要求事先明肯定義好各項屬性,而且以後咱們沒法動態添加新的屬性,也沒法刪除屬性,咱們惟一能作的就是更改屬性值。相反,咱們使用字典,那麼須要咱們自定義模型的屬性在字典中就是一系列的鍵值對,這樣就有很大的操做性了。咱們能夠自由的作增刪改操做。
固然並非全部的模型對象均可以經過 NSDictionary 來描述。大部分模型對象具備嚴格的定義和特殊的數據處理方式,不適合採用簡單的鍵值對來管理數據。
對於查詢表。咱們可能會在代碼中見過這樣的代碼
- (void)changeCharacterClass:(id)sender { NSString * enterText = nil; NSString * cc = nil; if ([enterText isEqualToString:@"W"]) { cc = @"w"; } else if ([enterText isEqualToString:@"A"]) { cc = @"a"; } else if ([enterText isEqualToString:@"B"]) { cc = @"b"; } }
可是當咱們須要編寫包含大量 if-else 或者 switch 語句的代碼時,一般應該考慮替換爲 NSDictionary 。字典能夠事先在兩組對象之間創建一對一的映射關係。
NSMutableDictionary *lookup = [[NSMutableDictionary alloc] init]; [lookup setObject:@"a" forKey:@"A"]; [lookup setObject:@"b" forKey:@"B"]; [lookup setObject:@"w" forKey:@"W"];
有了 lookup 查詢表, changeCharacterClass 方法就會變得很簡單了
- (void)changeCharacterClass:(id)sender { NSString * enterText = nil; NSString * cc = nil; cc = [lookup objectForKey:enterText]; }
使用 NSDictionary 查詢表的另外一個優勢就是:不須要再方法中硬編碼全部數據,相反,能夠將數據保存在文件系統或者遠程服務器中,甚至能夠由用戶動態添加或者編輯等。
JXImageStore 將使用 NSDictionary 查詢表存儲照片。JXImageStore 將會爲每一張照片生成惟一的鍵,以後能夠經過鍵來查找對應的照片。
將照片加入 JXImageStore 對象時,須要針對不一樣的照片使用不用的鍵,而後將這個賦值給響應的 JXItem 對象。當 JXDetailViewController 對象要從 JXImageStore 對象載入照片時,須要先從 JXItem 對象獲得照片的鍵,而後經過 JXImageStore 對象查詢相對應的值。
#import <Foundation/Foundation.h> @interface JXItem : NSObject /** 建立日期 */ @property (nonatomic,strong,readonly) NSDate * createDate; /** 名稱 */ @property (nonatomic,strong) NSString * itemName; /** 編號 */ @property (nonatomic,strong) NSString * serialnumber; /** 價值 */ @property (nonatomic,assign) NSInteger valueInDollars; /** JXImageStore中的鍵 */ @property (nonatomic,strong) NSString * itemKey; + (instancetype)randomItem; /** * JXItem類指定的初始化方法 * @return 類對象 */ - (instancetype)initWithItemName:(NSString *)name valueInDollars:(NSInteger)value serialNumber:(NSString *)sNumber; - (instancetype)initWithItemName:(NSString *)name; @end
照片的鍵不能重複,不然沒法經過咱們建立的對象準確的保存照片信息。這裏咱們將使用 Cocoa Touch 提供的一種機制來生成無重複的標識。這種機制能夠生成惟一標識(UUID,也叫 GUID)。每一個 NSUUID 類的對象都標識一個惟一的 UUID 。UUID是基於時間,計數器,和硬件標識(一般爲無線網卡的MAC地址)
等數據計算生成的。
#import "JXDetailViewController.h" #import "JXItem.h" #import "JXImageStore.h" @interface JXDetailViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate> @property (weak, nonatomic) IBOutlet UITextField *nameField; @property (weak, nonatomic) IBOutlet UITextField *seriaNumberField; @property (weak, nonatomic) IBOutlet UITextField *valueField; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIToolbar *toolbar; @end
#import "JXItem.h" @implementation JXItem + (instancetype)randomItem { // 建立不可變數組對象,包含三個形容詞 NSArray * randomAdjectiveList = @[ @"Fluffy", @"Rusty", @"Shiny" ]; // 建立不可變數組對象,包含三個名詞 NSArray * randomNounList = @[ @"Bear", @"Spork", @"Mac" ]; // 根據數組對象所含的對象的個數,獲得隨機索引 // 注意:運算符%是模運算符,運算後獲得的是餘數 NSInteger adjectiveIndex = arc4random() % randomAdjectiveList.count; NSInteger nounIndex = arc4random() % randomNounList.count; // 注意,類型爲NSInteger 的變量不是對象 NSString * randomName = [NSString stringWithFormat:@"%@ %@",randomAdjectiveList[adjectiveIndex],randomNounList[nounIndex]]; NSInteger randomValue = arc4random_uniform(100); NSString * randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c", '0' + arc4random_uniform(10), 'A' + arc4random_uniform(26), '0' + arc4random_uniform(10), 'A' + arc4random_uniform(26)]; JXItem * newItem = [[self alloc] initWithItemName:randomName valueInDollars:randomValue serialNumber:randomSerialNumber]; return newItem; } - (NSString *)description { NSString * descriptionString = [NSString stringWithFormat:@"%@ (%@):Worth $%zd, recorded on %@",self.itemName,self.serialnumber,self.valueInDollars,self.createDate]; return descriptionString; } - (instancetype)initWithItemName:(NSString *)name valueInDollars:(NSInteger)value serialNumber:(NSString *)sNumber { // 調用父類的指定初始化方法 self = [super init]; // 父類的指定初始化方法是否成功建立了對象 if (self) { // 爲實例變量設置初始值 _itemName = name; _valueInDollars = value; _serialnumber = sNumber; // 設置_createDate爲當前時間 _createDate = [NSDate date]; // 建立一個 NSUUID 對象 NSUUID * uuid = [[NSUUID alloc] init]; NSString * key = [uuid UUIDString]; _itemKey = key; } // 返回初始化後的對象的新地址 return self; } - (instancetype)initWithItemName:(NSString *)name { return [self initWithItemName:name valueInDollars:0 serialNumber:@""]; } - (instancetype)init { return [self initWithItemName:@"Item"]; } - (void)dealloc { NSLog(@"Destoryed:%@",self); } @end
#import "JXDetailViewController.h" #import "JXItem.h" #import "JXImageStore.h" @interface JXDetailViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate> @property (weak, nonatomic) IBOutlet UITextField *nameField; @property (weak, nonatomic) IBOutlet UITextField *seriaNumberField; @property (weak, nonatomic) IBOutlet UITextField *valueField; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIToolbar *toolbar; @end @implementation JXDetailViewController - (IBAction)takePicture:(id)sender { UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init]; // 若是設備支持相機,就使用拍照模式 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; } else { imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; } imagePicker.delegate = self; [self presentViewController:imagePicker animated:YES completion:nil]; } - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info { // 經過 info 字典獲取選中的照片 UIImage * image = info[UIImagePickerControllerOriginalImage]; // 以 itemKey 爲鍵,將照片存到自定義類中 [[JXImageStore sharedStore] setImage:image forKey:self.item.itemKey]; // 將照片放入 UIImageView 對象 self.imageView.image = image; // 關閉 UIImagePickerController 對象 [self dismissViewControllerAnimated:YES completion:nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 取消當前的第一響應對象 [self.view endEditing:YES]; // 將修改保存到 JXItem JXItem * item = self.item; item.itemName = self.nameField.text; item.serialnumber = self.seriaNumberField.text; item.valueInDollars = [self.valueField.text integerValue]; } - (void)viewDidLoad { [super viewDidLoad]; JXItem * item = self.item; self.nameField.text = item.itemName; self.seriaNumberField.text = item.itemName; self.valueField.text = [NSString stringWithFormat:@"%ld",item.valueInDollars]; // 建立 NSDdateFoemateter 對象,用於將 NSDate 對象轉換成簡單的日期字符串 static NSDateFormatter * dateFormatter = nil; if (!dateFormatter) { dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterNoStyle; } // 將轉換後獲得的日期字符串設置爲 dateLabel 的標題 self.dateLabel.text = [dateFormatter stringFromDate:item.createDate]; } - (void)setItem:(JXItem *)item { _item = item; self.navigationItem.title = _item.itemName; } @end
每當 JXDetailViewController 獲取到 UIImage 對象以後,都會將其存入到 JXImageStore 對象。
相似的,在用戶刪除了某個 JXItem 對象後,須要同時在 JXImageStore 對象中刪除對應的 UIImage 對象。
#import "JXItemStore.h" #import "JXItem.h" #import "JXImageStore.h" @interface JXItemStore () /** 可變數組,用來操做 JXItem 對象 */ @property (nonatomic,strong) NSMutableArray * privateItems; @end @implementation JXItemStore // 單粒對象 + (instancetype)sharedStore { static JXItemStore * sharedStore = nil; // 判斷是否須要建立一個 sharedStore 對象 if (!sharedStore) { sharedStore = [[self alloc] init]; } return sharedStore; } - (NSArray *)allItem { return [self.privateItems copy]; } - (JXItem *)createItem { JXItem * item = [JXItem randomItem]; [self.privateItems addObject:item]; return item; } /** * 還能夠調用 [self.privateItems removeObject:item] * [self.privateItems removeObjectIdenticalTo:item] 與上面的方法的區別就是:上面的方法會枚舉數組,向每個數組發送 isEqual: 消息。 * isEqual: 的做用是判斷當前對象和傳入對象所包含的數據是否相等。可能會複寫 這個方法。 * removeObjectIdenticalTo: 方法不會比較對象所包含的數據,只會比較指向對象的指針 * * @param item 須要刪除的對象 */ - (void)removeItem:(JXItem *)item { [self.privateItems removeObjectIdenticalTo:item]; [[JXImageStore sharedStore] deleteImageForKey:item.itemKey]; } - (void)moveItemAtIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex { // 若是起始位置和最終位置相同,則不懂 if (fromIndex == toIndex) return; // 須要移動的對象的指針 JXItem * item = self.privateItems[fromIndex]; // 將 item 從 allItem 數組中移除 [self.privateItems removeObjectAtIndex:fromIndex]; // 根據新的索引位置,將item 插入到allItem 數組中 [self.privateItems insertObject:item atIndex:toIndex]; } #pragma mark - 懶加載 - (NSMutableArray *)privateItems{ if (_privateItems == nil) { _privateItems = [[NSMutableArray alloc] init]; } return _privateItems; } @end
當 JXHomepwner 須要顯示 JXDetailViewController 對象的視圖時,該對象須要經過當前選中的 JXItem 對象的 itemKey 屬性來從 JXImageStore 對象中獲得相應的照片。
#import "JXDetailViewController.h" #import "JXItem.h" #import "JXImageStore.h" @interface JXDetailViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate> @property (weak, nonatomic) IBOutlet UITextField *nameField; @property (weak, nonatomic) IBOutlet UITextField *seriaNumberField; @property (weak, nonatomic) IBOutlet UITextField *valueField; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIToolbar *toolbar; @end @implementation JXDetailViewController - (IBAction)takePicture:(id)sender { UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init]; // 若是設備支持相機,就使用拍照模式 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; } else { imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; } imagePicker.delegate = self; [self presentViewController:imagePicker animated:YES completion:nil]; } - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info { // 經過 info 字典獲取選中的照片 UIImage * image = info[UIImagePickerControllerOriginalImage]; // 以 itemKey 爲鍵,將照片存到自定義類中 [[JXImageStore sharedStore] setImage:image forKey:self.item.itemKey]; // 將照片放入 UIImageView 對象 self.imageView.image = image; // 關閉 UIImagePickerController 對象 [self dismissViewControllerAnimated:YES completion:nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 取消當前的第一響應對象 [self.view endEditing:YES]; // 將修改保存到 JXItem JXItem * item = self.item; item.itemName = self.nameField.text; item.serialnumber = self.seriaNumberField.text; item.valueInDollars = [self.valueField.text integerValue]; } - (void)viewDidLoad { [super viewDidLoad]; JXItem * item = self.item; self.nameField.text = item.itemName; self.seriaNumberField.text = item.itemName; self.valueField.text = [NSString stringWithFormat:@"%ld",item.valueInDollars]; // 建立 NSDdateFoemateter 對象,用於將 NSDate 對象轉換成簡單的日期字符串 static NSDateFormatter * dateFormatter = nil; if (!dateFormatter) { dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterNoStyle; } // 將轉換後獲得的日期字符串設置爲 dateLabel 的標題 self.dateLabel.text = [dateFormatter stringFromDate:item.createDate]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; JXItem * item = self.item; // 根據 itemKey,獲取照片 UIImage * imageToDisplay = [[JXImageStore sharedStore] imageForKey:item.itemKey]; // 將獲得的圖片賦值 self.imageView.image = imageToDisplay; } - (void)setItem:(JXItem *)item { _item = item; self.navigationItem.title = _item.itemName; } @end
#import "JXDetailViewController.h" #import "JXItem.h" #import "JXImageStore.h" @interface JXDetailViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate,UITextFieldDelegate> @property (weak, nonatomic) IBOutlet UITextField *nameField; @property (weak, nonatomic) IBOutlet UITextField *seriaNumberField; @property (weak, nonatomic) IBOutlet UITextField *valueField; @property (weak, nonatomic) IBOutlet UILabel *dateLabel; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet UIToolbar *toolbar; @end @implementation JXDetailViewController - (IBAction)takePicture:(id)sender { UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init]; // 若是設備支持相機,就使用拍照模式 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; } else { imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; } imagePicker.delegate = self; [self presentViewController:imagePicker animated:YES completion:nil]; } - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info { // 經過 info 字典獲取選中的照片 UIImage * image = info[UIImagePickerControllerOriginalImage]; // 以 itemKey 爲鍵,將照片存到自定義類中 [[JXImageStore sharedStore] setImage:image forKey:self.item.itemKey]; // 將照片放入 UIImageView 對象 self.imageView.image = image; // 關閉 UIImagePickerController 對象 [self dismissViewControllerAnimated:YES completion:nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // 取消當前的第一響應對象 [self.view endEditing:YES]; // 將修改保存到 JXItem JXItem * item = self.item; item.itemName = self.nameField.text; item.serialnumber = self.seriaNumberField.text; item.valueInDollars = [self.valueField.text integerValue]; } - (void)viewDidLoad { [super viewDidLoad]; JXItem * item = self.item; self.nameField.text = item.itemName; self.seriaNumberField.text = item.itemName; self.valueField.text = [NSString stringWithFormat:@"%ld",item.valueInDollars]; // 建立 NSDdateFoemateter 對象,用於將 NSDate 對象轉換成簡單的日期字符串 static NSDateFormatter * dateFormatter = nil; if (!dateFormatter) { dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterNoStyle; } // 將轉換後獲得的日期字符串設置爲 dateLabel 的標題 self.dateLabel.text = [dateFormatter stringFromDate:item.createDate]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; JXItem * item = self.item; // 根據 itemKey,獲取照片 UIImage * imageToDisplay = [[JXImageStore sharedStore] imageForKey:item.itemKey]; // 將獲得的圖片賦值 self.imageView.image = imageToDisplay; } - (void)setItem:(JXItem *)item { _item = item; self.navigationItem.title = _item.itemName; } - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return YES; } @end
前面咱們知道了如何解決拍照、保存、讀取等問題。如今咱們簡單瞭解一下攝像的問題。
UIImagePickerController 對象能夠選擇的媒體類型又兩種,分別爲靜態照片和視頻。 mediaTypes 數組默認只包含產概念股字符串 kUTTypeImage 所以,若是不修改對象的 mediaTypes 屬性,那麼用戶就只可以使用相機拍攝照片。
添加攝像只需將常量字符串 kUTTypeMovie 加入到 mediaTypes 數組中便可。對於那些不支持攝像的設備(那麼咱們就放棄吧)一樣可使用
+ (nullable NSArray<NSString *> *)availableMediaTypesForSourceType:(UIImagePickerControllerSourceType)sourceType; // returns array of available media types (i.e. kUTTypeImage)
使用
UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init]; NSArray * availableTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera]; imagePicker.mediaTypes = availableTypes; imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; imagePicker.delegate = self;
加入攝像功能的 UIImagePickerController 界面會多出一個開關,能夠在照相模式或者攝像模式之間切換。若是咱們選中的是攝像模式,就須要在
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
處理結果。當咱們處理的是靜態照片的時候,傳入到上述方法中的 info 參數會有包含一個 UIImage 對象,以對應整張圖片。可是針對攝像,UIIImagePickerController 對象會將拍攝到的視頻存入臨時目錄,由於移動內存有限。當用戶拍攝結束的時候,該對象的委託對象就會收到上述消息,而且在 info 參數中會有一個包含視頻的文件路徑,獲取方式
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info { NSURL * mediaURL = info[UIImagePickerControllerMediaURL]; }
可是臨時目錄是不安全的,隨時可能會被清楚,因此咱們應該將其移動到其餘目錄
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info { NSURL * mediaURL = info[UIImagePickerControllerMediaURL]; if (mediaURL) { // 肯定設備支持視頻 if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum([mediaURL path])) { // 將視頻存入相冊 UISaveVideoAtPathToSavedPhotosAlbum([mediaURL path], nil, nil, nil); // 刪除臨時目錄下的視頻 [[NSFileManager defaultManager] removeItemAtPath:[mediaURL path] error:nil]; } } }