zoukankan      html  css  js  c++  java
  • iOS 相机

      本章节主要为之前项目 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 = 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];
            }
        }
    }
  • 相关阅读:
    java MessageFormat来生成模板字符串
    linux 用户身份切换
    linux 账号管理
    java 模块化
    mysql 存储过程执行while循环 Lost connection to MySQL server during query
    git 加速
    测试目录
    centos 安装gitblit
    centos7 安装mongoDB
    MySQL 批量修改库、表、列的排序规则,处理数据库大小写不敏感问题。
  • 原文地址:https://www.cnblogs.com/wang-com/p/5931988.html
Copyright © 2011-2022 走看看