iOS 相機

  本章節主要爲以前項目 JXHomepwner 添加照片功能(項目地址)。具體任務就是顯示一個 UIImagePickerController 對象,使用戶可以爲 JXItem 對象拍照並保存。拍攝的照片會和相應的 JXItem 對象創建關聯,當用戶進入某個 JXItem 對象的詳細視圖的時候,能夠看見以前拍攝的照片。git

  照片的文件可能很大,最後與 JXItem 對象的其餘數據分開保存。咱們將創建一個用於存儲數據的類 JXImageStore ,負責保存 JXItem 對象的照片。JXImageStore 能夠按須要獲取並緩存照片,還能夠在設備內存太低的時候清空緩存中的照片。github

  • 經過 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 對象的個數。數據結構

  創建關聯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
  • 經過 UIImagePickerController 拍攝照片

  接下來,咱們須要在  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

  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 = 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
  • 使用 JXImageStore

  當 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];
        }
    }
}
相關文章
相關標籤/搜索