本章節主要為之前項目 JXHomepwner 添加照片功能(項目地址)。具體任務就是顯示一個 UIImagePickerController 對象,使用戶能夠為 JXItem 對象拍照並保存。拍攝的照片會和相應的 JXItem 對象建立關聯,當用戶進入某個 JXItem 對象的詳細視圖的時候,可以看 ...
本章節主要為之前項目 JXHomepwner 添加照片功能(項目地址)。具體任務就是顯示一個 UIImagePickerController 對象,使用戶能夠為 JXItem 對象拍照並保存。拍攝的照片會和相應的 JXItem 對象建立關聯,當用戶進入某個 JXItem 對象的詳細視圖的時候,可以看見之前拍攝的照片。
照片的文件可能很大,最後與 JXItem 對象的其他數據分開保存。我們將建立一個用於存儲數據的類 JXImageStore ,負責保存 JXItem 對象的照片。JXImageStore 可以按需要獲取並緩存照片,還可以在設備記憶體過低的時候清空緩存中的照片。
- 通過 UIImageView 對象顯示照片
首先要將照片賦值給 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 對象的個數。
建立關聯
關聯之後代碼如下
#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
- 通過 UIImagePickerController 拍攝照片
接下來,我們需要在 takePicture: 方法中創建並顯示 UIImagePickerController 對象。創建該對象時,必須為新創建的對象設置 sourceType 屬性和 delegate 屬性。
設置 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
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
- NSDictionary
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 =