iOS 相機

来源:http://www.cnblogs.com/wang-com/archive/2016/10/05/5931988.html
-Advertisement-
Play Games

本章節主要為之前項目 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 =

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 網上有很多例子來演示Android客戶端和伺服器端數據如何實現交互不過這些例子大多比較繁雜,對於初學者來說這是不利的,現在介紹幾種代碼簡單、邏輯清晰的交互例子,本篇博客介紹第四種: 一、伺服器端: 代碼1:添加名為“AndroidServerServlet.Java”的文件 [java] view ...
  • 1.先重寫uiviewcontrol的方法 - (UIViewController *)viewController { for (UIView* next = [self superview]; next; next = next.superview) { UIResponder *nextRes ...
  • 概述在動畫中,我們會指定動畫的持續時間。例如scaleAnimation.duration = self.config.appearDuration那麼這個時間是怎麼定義的呢?是指的絕對時間嗎?層級時間結構layer在屏幕上的顯示位置是根據父layer的位置以及本身相對於父layer偏移定義的。與此... ...
  • iOS多線程技術方案 === 目錄 "一、多線程簡介" "1、多線程的由來" "2、耗時操作的模擬試驗" "3、進程和線程" "4、多線程的概念及原理" "5、多線程的優缺點和一個Tip" "6、主線程" "7、技術方案" "二、Pthread" "1、函數" "2、參數和返回值" "3、使用" " ...
  • 眾所周知,viewPager是能夠滑動的,但有時候我們需要禁止它的滑動(微笑地面對*—……—*)。 情況是這樣的: activity中有一個viewPager,viewPager中加入3個Fragment,第三個Fragment中又使用了一個viewPager,這個viewPager中又加入了幾個F ...
  • 【框架】: 公共部分:左側菜單、TitleBar、RadioGroup(3個RadioButton:X、Y、Z) 選擇X頁面:指示器+ViewPager 【要達成的效果】: (1)左側選擇A,進入X頁面,X1聯網刷新頁面,此時禁止X2預載入—>滑動到X2頁面,X2才聯網刷新—>X3—>X4; (2) ...
  • Activty啟動提供了四種啟動模式。launchMode: standard:每次啟動新的活動視窗(new操作) singleTop:如果在棧頂是目標活動,則直接打開.否則開啟新的活動視窗(new). singleTask和singleInstance基本上相同.差別在於若根活動設置為single ...
  • 當某個activity變得“容易”被系統銷毀時,該activity的onSaveInstanceState就會被執行,除非該activity是被用戶主動銷毀的,例如當用戶按BACK鍵的時候。 註意上面的雙引號,何為“容易”?言下之意就是該activity還沒有被銷毀,而僅僅是一種可能性。這種可能性有 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...