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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...