SlideShare a Scribd company logo
1 of 45
Download to read offline
Camera




范圣刚,princetoad@gmail.com, www.tfan.org
• 在这个主题我们将给 Homepwner 增加照⽚片
• 我们将呈现⼀一个 UIImagePickerController,这样⽤用
 户就可以获取并保存每个 item 的照⽚片。

• 这个图⽚片会被关联到⼀一个 BNRItem 实例,存储到
 ⼀一个 image store 中,并且在 item 的详细视图中可
 以看到
带图⽚片的 Homepwner
图⽚片显⽰示和 UIImageView
• 要显⽰示图⽚片,⼀一个简单的⽅方法就是把⼀一个
 UIImageView 放到屏幕上。打开 Hompwner 项
 ⺫⽬目,和 DetailViewController.xib,拖拽⼀一个
 UIImageView 到 view 上
contentMode - Aspect Fit
• UIImageView 根据它的 contentMode 属性来显⽰示图
 ⽚片,这个属性决定了如何定位以及如何在它的
 frame 内调整内容的⼤大⼩小。
• contentMode 默认值是
 UIViewContentModeScaleToFill,它将调整图⽚片来
 正好匹配 image view 的 bounds。为了不使图⽚片变
 形,我们把它改成 “Aspect Fit”
连接到 view controller
    • 控件设置好了以后,我们⽤用前⾯面提到的从
    UIImageView Control-drag 到
    DetailViewController.h 的实例变量区域的⽅方法,⽣生
    成⼀一个名为 imageView 的 outlet,选择 Weak 作为
    存储类型
@interface DetailViewController : UIViewController
<UINavigationControllerDelegate, UIImagePickerControllerDelegate,
UITextFieldDelegate>
{
    __weak IBOutlet UITextField *nameField;

    __weak IBOutlet UITextField *serialNumberField;

    __weak IBOutlet UILabel *dateLabel;
    __weak IBOutlet UITextField *valueField;

    __weak IBOutlet UIImageView *imageView;
}
拍照和 UIImagePickerController
• 我们需要⼀一个按钮来启动照⽚片获取的过程:在
 DetailViewController.xib 中,拖动⼀一个 UIToolbar
 到 DetailViewController 的 view 的底部

• UIToolbar 和 UINavigationBar 类似的是我们都可以
 给它添加 UIBarButtonItems。

• 不同的是,导航栏针对 bar button item 只有两个
 插槽,⽽而 toolbar 有⼀一个 bar button item 的数组,
 只要屏幕放的下我们可以往⾥里⾯面添加尽量多的
 UIBarButtonItem
Identifier -> Camera
• 默认情况下,在 XIB ⽂文件中新⽣生成的 UIToolbar 实
 例会带⼀一个 UIBarButtonItem。
• 选中这个 bar button item,打开 attribute
 inspector,把它的 Identifier 改成 Camera,这个
 item 就会显⽰示⼀一个 camera 图标
带 bar button item 的 UIToolbar
camera 按钮⽅方法
• 有了按钮以后,我们要在代码中声明它触发的⽅方
 法
• 选中这个 camera 按钮,然后从按钮 Control-drag
 到 DetailViewController.h 中⽅方法声明的区域
• 选择 Action,⽅方法命名为:takePicture:
• 这样 camera 按钮按下时就会发送这个消息给
 DetailViewController。
• ⽤用这种⽅方式连接⼀一个 action ⽅方法,同时会⾃自动在
 DetailViewController.m 中添加⼀一个 stub
 implementation
UIImagePickerController
• camera 按钮的⽅方法有了之后,我们就得看⼀一下怎
 么在这个 takePicture: ⽅方法⾥里⾯面实现拍照(或选取
 照⽚片)
• 我们可以使⽤用 UIImagePickerController,调⽤用系统
 本⾝身的应⽤用来获取照⽚片。
• 当创建这个类的实例时,我们必须指定它的
 sourceType 属性,并且分配给它⼀一个 delegate
sourceType 属性
• sourceType 是⼀一个⽤用来告诉 image picker 到哪⾥里
 获得照⽚片的常量:
 • UIImagePickerControllerSourceTypeCamera - 拍新照
  ⽚片
 • UIImagePickerControllerSourceTypeSavedPhotoAlbu
  m 和 UIImagePickerControllerSourceTypePhotoLibrary
  都是从保存的照⽚片中选取
• 在开始使⽤用
 UIImagePickerControllerSourceTypeCamera 之前要先
 判断设备有没有摄像头。

• ⽅方法是以 sourceType 常量作为参数发送
 isSourceTypeAvailable: 消息给
 UIImagePickerController
delegate 和消息
• 除了 sourceType 之外,UIImagePickerController 的
 实例还需要⼀一个 delegate 来处理来⾃自它的 view 的
 请求。
• 当⽤用户在 UIImagePickerController 界⾯面确认选取的
 图⽚片后,
 imagePickerController:didFinishPickingMediaWithInfo
 : 消息被发送给它的 delegate。(如果这个过程被
 cancel 掉的话,消息是:
 imagePickerControllerDidCancel:)
• 在 takePicker: 中增加初始化和设置 delegate 的代
 码
imagePicker 的初始化和设置
- (IBAction)takePicture:(id)sender {
    UIImagePickerController *imagePicker = [[UIImagePickerController alloc]
init];
    // 如果设备有 camera 的话,我们就采集⼀一个图⽚片,否则我们从 photo library 从拾取⼀一
个
    if ([UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        [imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
    } else {
        [imagePicker
setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
    }

    [imagePicker setDelegate:self];
}


    • 初始化和设置完成之后,下⼀一步就是要把它显⽰示
    出来。imagePicker 和之前⽤用到的 UIViewController
    ⼦子类不同的是它要模态化呈现。
    • 模态化:占满全屏,直到完成⼯工作。
显⽰示 imagePicker
    • 要模态化的呈现⼀一个视图,发送
     presentViewController:animated:completion: 消
     息给当前其 view 在屏幕上显⽰示的
     UIViewController。
    • 传递的第⼀一个参数就是要呈现的 view controller,
     它的 view 会从屏幕底部向上滑动出来
    • 在 takePicture: 末尾增加显⽰示 imagePicker 的代码
     [imagePicker setDelegate:self];

     // 把 image picker 放到屏幕上(模态对话框)
     [self presentViewController:imagePicker animated:YES completion:nil];
}
UINavigationControllerDelegate
 • imagePicker 的初始化,设置和显⽰示都完成了,下
  ⼀一步就是获取 imagePicker 选择的图⽚片
 • 需要在 DetailViewController 中实现我们前⾯面提到
  的
  imagePickerController:didFinishPickingMediaWithInfo
  :




• UIImagePickerController 是 UINavigationController
 的⼦子类,在类声明中添加下⻚页这些 protocol
image 的独⽴立存储
@interface DetailViewController : UIViewController
<UINavigationControllerDelegate, UIImagePickerControllerDelegate>
{


 • 我们可以直接在didFinishPickingMediaWithInfo: 中
    直接把选择的 image 设置给 imageView
 • 但是我们最好为 image 创建⼀一个单独的存储。
• 我们把图⽚片放到 store 中,这样当
 DetailViewController 的 view 下次在屏幕上显⽰示
 时,我们让 DetailViewController 从 image store 中
 提取图⽚片,然后放到它⾃自⼰己的 imageView

• ⼀一般来讲,每次⼀一个 view controller 收到
 viewWillAppear: 消息时都应该重新使⽤用数据填充
 它的视图的⼦子视图
创建BNRImageStore
• 这样我们在真正显⽰示选中的图⽚片前,先创建⼀一个
       store 对它进⾏行管理。

 • image store 将会持有⽤用户会⽤用到的全部图⽚片。
 • ⽣生成⼀一个名为 BNRImageStore 的 NSObject 的⼦子类
 • ⽣生成下列接⼝口:
@interface BNRImageStore : NSObject
{
    NSMutableDictionary *dictionary;
}

+ (BNRImageStore *)sharedStore;

- (void)setImage:(UIImage *)i forKey:(NSString *)s;
- (UIImage *)imageForKey:(NSString *)s;
- (void)deleteImageForkey:(NSString *)s;

@end
实现 Singleton
 • 和 BNRItemStore ⼀一样,BNRImageStore 也需要是
   单例的。在 BNRImageStore.m 中编写下⾯面这些和
   BNRItemStore 类似的代码
+ (id)allocWithZone:(NSZone *)zone
{
    return [self sharedStore];
}
+ (BNRImageStore *)sharedStore
{
    static BNRImageStore *sharedStore = nil;
    if (!sharedStore) {
        sharedStore = [[super allocWithZone:NULL] init];
    }
    return sharedStore;
}
- (id)init
{
    self = [super init];
    if (self) {
        dictionary = [[NSMutableDictionary alloc] init];
    }
    return self;
}
实现头⽂文件中声明的另外三个⽅方法
- (void)setImage:(UIImage *)i forKey:(NSString *)s
{
    [dictionary setObject:i forKey:s];
}

- (UIImage *)imageForKey:(NSString *)s
{
    return [dictionary objectForKey:s];
}

- (void)deleteImageForkey:(NSString *)s
{
    if (!s) {
        return;
    }
    [dictionary removeObjectForKey:s];
}


 • setImage: 增加(更新)
 • imageForKey: 获取
 • deleteImageForKey: 删除
NSMutableDictionary
• 经过上⾯面⼀一些操作,基本的 image store 功能就完
 成了。
• 这⾥里⾯面我们使⽤用的是 NSMutableDictionary 来存
 储数据(key-value pair)
• key 是⼀一个唯⼀一值(⼀一般是字符串),value 是被
 存储在集合中的对象
NSDictionary
⽣生成和使⽤用键
• 要使⽤用这个 image store,⾸首先我们要想办法能够
    ⽣生成唯⼀一的 key 。
    • ⼀一个是因为在把图⽚片存到字典时要⽤用到;
    • 另外要把这个 key 给相关联的 BNRItem,在
      DetailViewController 需要从 image store 拿到⼀一张图⽚片
      时,要询问它的 item key 是多少,然后使⽤用这个 key
      在字典中搜索图⽚片

 • ⾸首先给 BNRItem 增加⼀一个属性来存储这个 key
@property (nonatomic, readonly, strong) NSDate *dateCreated;

@property (nonatomic, copy) NSString *imageKey;

@implementation BNRItem
@synthesize imageKey;
CFUUIDRef 和 CFUUIDCreate
 • 要保证 key 的唯⼀一性,可以使⽤用 UUID 的⽅方式
 • CFUUIDRef 类型的对象表⽰示⼀一个 UUID,下⾯面在
    imagePickerController:didFinishPickingMediaWithInfo
    : 中⽣生成⼀一个 UUID
    UIImage *image = [info
objectForKey:UIImagePickerControllerOriginalImage];

    // ⽣生成⼀一个 CFUUID 对象
    CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault);


• CF对象的创建通过调⽤用以被创建的对象的类型开
  始,并且包含“Create”的函数(CFUUIDCreate)
• 指定的第⼀一个参数是内存如何分配,传⼊入
  kCFAllocateDefault 表⽰示由系统决定
CFUUIDCreateString 和 CFStringRef
 • CFUUIDRef 是⼀一个字节数组(15个uint8)
 • 因为我们要把 UUID 作为字典的 key,以及在后⾯面
   存储到⽂文件系统的时候会把它作为⽂文件名,所以
   需要把 UUID 转成字符串
 • 可以通过调⽤用 CFUUIDCreateString 从 CFUUIDRef
   ⽣生成⼀一个字符串对象
    // ⽣生成⼀一个 CFUUID 对象
    CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault);
    // 从 unique identifier ⽣生成⼀一个字符串
    CFStringRef newUniqueIDString = CFUUIDCreateString(kCFAllocatorDefault,
newUniqueID);
类型转换
 • CFStringRef 和 NSString 需要相互转换
 • 转换类型,把它设置成选中的 BNRItem 的
   imageKey,同时把 image 放到 BNRImageStore 中
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    UIImage *image = [info
objectForKey:UIImagePickerControllerOriginalImage];

    CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault);
    CFStringRef newUniqueIDString = CFUUIDCreateString(kCFAllocatorDefault,
newUniqueID);
    // CFStringRef -> NSString
    // 使⽤用这个 unique ID 来设置 item 的 imageKey
    NSString *key = (__bridge NSString *)newUniqueIDString;
    [item setImageKey:key];

    // 使⽤用这个 key 把图⽚片保存到 BNRImageStore
    [[BNRImageStore sharedStore] setImage:image forKey:[item imageKey]];
Core Foundation 和 toll-free
         bridging
• 当⼀一个指向 Objective-C 对象的变量被销毁时,
 ARC 知道这个对象失去了⼀一个所有者。

• 但是 ARC 并不对 Core Foundation 的对象做这些
• 这样当 Core Foundation 的对象失去指针时,我们
 必须在丢掉这个指针前调⽤用⼀一个函数来告诉对象
 丢掉⼀一个所有者
• 如果我们没有在失去指针前调⽤用 CFRelease ,被指
向的对象仍然认为它有⼀一个所有者

• 在告诉它丢掉⼀一个所有者之前丢掉指针会引起内
存泄露:对象已经访问不了了,但还有所有者
CFRelease
• 增加代码,告诉被 newUniqueIDString 和
 newUniqueID 指向的对象丢掉⼀一个所有者,因为
 都是局部变量,⽅方法结束将销毁
 [[BNRImageStore sharedStore] setImage:image forKey:[item imageKey]];

 // lose owner
 CFRelease(newUniqueIDString);
 CFRelease(newUniqueID);

 // 把图⽚片放到我们屏幕上的 image view 中
 [imageView setImage:image];
__bridge
 NSString *key = (__bridge NSString *)newUniqueIDString;



• ARC 并不很会管理 Core Foundation 对象的内存
• 在我们类型转换⼀一个 Core Foundation 指针到 它的
 Objective-C 同级形式时,放⼀一个 __bridge 在调⽤用
 前⾯面,是告诉 ARC 不要像通常做的那样给 key 变
 量⼀一个所有权
结束 BNRImageStore
• 现在 BNRItemStore 可以存储图⽚片,并且 BNRItem
 有获得这个图⽚片的 key,我们需要告诉
 DetailViewController 怎样从选中的 BNRItem 抓取
 图⽚片,并放⼊入它的 imageView
Cache
获取并显⽰示照⽚片
 • DetailViewController 的 view 将会在两个时间上出
   现:当⽤用户轻击 ItemViewController 中的⼀一⾏行时,
   当 UIImagePickerController 被释放时
 • 在两种情况下,imageView 都应该被正在显⽰示的
   BNRItem 的图⽚片填充。在 DetailViewController.m
   中增加代码到 viewWillAppear:
    // 显⽰示图⽚片
    NSString *imageKey = [item imageKey];
    if (imageKey) {
        UIImage *imageToDisplay = [[BNRImageStore sharedStore]
imageForKey:imageKey];
        [imageView setImage:imageToDisplay];
    } else {
        [imageView setImage:nil];
    }
删除旧图⽚片
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    // 如果存在旧图⽚片,先把它从 BNRImageStore 中删除
    NSString *oldKey = [item imageKey];
    if (oldKey) {
        [[BNRImageStore sharedStore] deleteImageForkey:oldKey];
    }

    // 从 info 字典中获得拾取的图⽚片
    UIImage *image = [info
objectForKey:UIImagePickerControllerOriginalImage];
释放键盘
• 让 DetailViewController 符合 UITextFieldDelegate
    protocol,实现 textFieldShouldReturn: ⽅方法来让⽂文
    本框字段在换⾏行键被按下时释放它的 first
    responder 状态以释放键盘
@interface DetailViewController : UIViewController
<UINavigationControllerDelegate, UIImagePickerControllerDelegate,
UITextFieldDelegate>

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}
UIControll 和 Touch Up Inside 事件
• 我们希望⽤用户在 DetailViewController 的 view 上的
 任何地⽅方轻击都释放键盘,我们可以通过给 view
 发送 endEditing: 消息实现释放键盘
• UIButton 可以在轻击时发送⼀一个 action 消息给它
 的 target,这种 target-action 特性是从 UIControl
 继承的
• 在 DetailViewController.xib 中选择 View 对象,在
 identity inspector 中把 view 的类改成 UIControl

• 然后从 view Control-drag 到⽅方法声明区域,事件
 选择 ”Touch Up Inside“
- (IBAction)backgroundTapped:(id)sender {
    [[self view] endEditing:YES];
}

More Related Content

Viewers also liked

Fix Memory Card Error
Fix Memory Card Error Fix Memory Card Error
Fix Memory Card Error photo recovery
 
Camera Work Explained
Camera Work ExplainedCamera Work Explained
Camera Work ExplainedEmilyNewson
 
Comparison between Human Eye and Camera
Comparison between Human Eye and CameraComparison between Human Eye and Camera
Comparison between Human Eye and CameraEdnexa
 
Camera Work Analysis
Camera Work AnalysisCamera Work Analysis
Camera Work Analysisbrinajohnson
 
Camera and Eye Comparison
Camera and Eye ComparisonCamera and Eye Comparison
Camera and Eye Comparisonguest5f2c73
 
Digital Camera Working Mechanism
Digital Camera Working MechanismDigital Camera Working Mechanism
Digital Camera Working MechanismMehedi3803
 
Digital cameras power point presentation
Digital cameras power point presentationDigital cameras power point presentation
Digital cameras power point presentationDavid Boin
 
Optics of human eye & refractive errors
Optics of human eye & refractive errorsOptics of human eye & refractive errors
Optics of human eye & refractive errorsSahithi Ganeshula
 
Camera work & shot types
Camera work & shot typesCamera work & shot types
Camera work & shot typesmichaeljfagan
 

Viewers also liked (11)

Fix Memory Card Error
Fix Memory Card Error Fix Memory Card Error
Fix Memory Card Error
 
L03 camera work
L03 camera workL03 camera work
L03 camera work
 
Camera work
Camera workCamera work
Camera work
 
Camera Work Explained
Camera Work ExplainedCamera Work Explained
Camera Work Explained
 
Comparison between Human Eye and Camera
Comparison between Human Eye and CameraComparison between Human Eye and Camera
Comparison between Human Eye and Camera
 
Camera Work Analysis
Camera Work AnalysisCamera Work Analysis
Camera Work Analysis
 
Camera and Eye Comparison
Camera and Eye ComparisonCamera and Eye Comparison
Camera and Eye Comparison
 
Digital Camera Working Mechanism
Digital Camera Working MechanismDigital Camera Working Mechanism
Digital Camera Working Mechanism
 
Digital cameras power point presentation
Digital cameras power point presentationDigital cameras power point presentation
Digital cameras power point presentation
 
Optics of human eye & refractive errors
Optics of human eye & refractive errorsOptics of human eye & refractive errors
Optics of human eye & refractive errors
 
Camera work & shot types
Camera work & shot typesCamera work & shot types
Camera work & shot types
 

Similar to 12 Camera

11 UINavigationController
11 UINavigationController11 UINavigationController
11 UINavigationControllerTom Fan
 
14 Saving Loading and Application States
14 Saving Loading and Application States14 Saving Loading and Application States
14 Saving Loading and Application StatesTom Fan
 
16 CoreData
16 CoreData16 CoreData
16 CoreDataTom Fan
 
01 A Simple iOS Application
01 A Simple iOS Application01 A Simple iOS Application
01 A Simple iOS ApplicationTom Fan
 
10 Editing UITableView
10 Editing UITableView10 Editing UITableView
10 Editing UITableViewTom Fan
 
KISSY_Component
KISSY_ComponentKISSY_Component
KISSY_Componentyiming he
 
Core data lightweight_migration
Core data lightweight_migrationCore data lightweight_migration
Core data lightweight_migrationMichael Pan
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejsChi-wen Sun
 
Introduction to ASP.NET MVC and MVC 5 Features
Introduction to ASP.NET MVC and MVC 5 FeaturesIntroduction to ASP.NET MVC and MVC 5 Features
Introduction to ASP.NET MVC and MVC 5 FeaturesJeff Chu
 
Ioc & in direction
Ioc & in directionIoc & in direction
Ioc & in direction育汶 郭
 
Html5移动网站开发实践
Html5移动网站开发实践Html5移动网站开发实践
Html5移动网站开发实践Web Zhao
 
Kissy editor开发与设计
Kissy editor开发与设计Kissy editor开发与设计
Kissy editor开发与设计yiming he
 

Similar to 12 Camera (20)

11 UINavigationController
11 UINavigationController11 UINavigationController
11 UINavigationController
 
14 Saving Loading and Application States
14 Saving Loading and Application States14 Saving Loading and Application States
14 Saving Loading and Application States
 
View Animation
View AnimationView Animation
View Animation
 
005
005005
005
 
I os 01
I os 01I os 01
I os 01
 
16 CoreData
16 CoreData16 CoreData
16 CoreData
 
I os 07
I os 07I os 07
I os 07
 
01 A Simple iOS Application
01 A Simple iOS Application01 A Simple iOS Application
01 A Simple iOS Application
 
10 Editing UITableView
10 Editing UITableView10 Editing UITableView
10 Editing UITableView
 
I os 10
I os 10I os 10
I os 10
 
Kinect+sdk
Kinect+sdkKinect+sdk
Kinect+sdk
 
KISSY_Component
KISSY_ComponentKISSY_Component
KISSY_Component
 
I os 16
I os 16I os 16
I os 16
 
Core data lightweight_migration
Core data lightweight_migrationCore data lightweight_migration
Core data lightweight_migration
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejs
 
I os 02
I os 02I os 02
I os 02
 
Introduction to ASP.NET MVC and MVC 5 Features
Introduction to ASP.NET MVC and MVC 5 FeaturesIntroduction to ASP.NET MVC and MVC 5 Features
Introduction to ASP.NET MVC and MVC 5 Features
 
Ioc & in direction
Ioc & in directionIoc & in direction
Ioc & in direction
 
Html5移动网站开发实践
Html5移动网站开发实践Html5移动网站开发实践
Html5移动网站开发实践
 
Kissy editor开发与设计
Kissy editor开发与设计Kissy editor开发与设计
Kissy editor开发与设计
 

More from Tom Fan

PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统Tom Fan
 
HTML5 Web workers
HTML5 Web workersHTML5 Web workers
HTML5 Web workersTom Fan
 
Web sockets
Web socketsWeb sockets
Web socketsTom Fan
 
Semantics
SemanticsSemantics
SemanticsTom Fan
 
Multimedia
MultimediaMultimedia
MultimediaTom Fan
 
Intro to-html5
Intro to-html5Intro to-html5
Intro to-html5Tom Fan
 
Html5 history
Html5 historyHtml5 history
Html5 historyTom Fan
 
Geolocation
GeolocationGeolocation
GeolocationTom Fan
 
File api
File apiFile api
File apiTom Fan
 
Deviceaccess
DeviceaccessDeviceaccess
DeviceaccessTom Fan
 
Webstorage
WebstorageWebstorage
WebstorageTom Fan
 
Html5 最重要的部分
Html5 最重要的部分Html5 最重要的部分
Html5 最重要的部分Tom Fan
 
AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状Tom Fan
 
PhoneGap 2.0 开发
PhoneGap 2.0 开发PhoneGap 2.0 开发
PhoneGap 2.0 开发Tom Fan
 
Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Tom Fan
 
HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型Tom Fan
 
18 NSUserDefaults
18 NSUserDefaults18 NSUserDefaults
18 NSUserDefaultsTom Fan
 
17 Localization
17 Localization17 Localization
17 LocalizationTom Fan
 

More from Tom Fan (20)

PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统PhoneGap 通信原理和插件系统
PhoneGap 通信原理和插件系统
 
HTML5 Web workers
HTML5 Web workersHTML5 Web workers
HTML5 Web workers
 
Web sockets
Web socketsWeb sockets
Web sockets
 
Storage
StorageStorage
Storage
 
Semantics
SemanticsSemantics
Semantics
 
Multimedia
MultimediaMultimedia
Multimedia
 
Intro to-html5
Intro to-html5Intro to-html5
Intro to-html5
 
Html5 history
Html5 historyHtml5 history
Html5 history
 
Geolocation
GeolocationGeolocation
Geolocation
 
File api
File apiFile api
File api
 
Deviceaccess
DeviceaccessDeviceaccess
Deviceaccess
 
Css3
Css3Css3
Css3
 
Webstorage
WebstorageWebstorage
Webstorage
 
Html5 最重要的部分
Html5 最重要的部分Html5 最重要的部分
Html5 最重要的部分
 
AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状AT&T 的 HTML5 策略和应用现状
AT&T 的 HTML5 策略和应用现状
 
PhoneGap 2.0 开发
PhoneGap 2.0 开发PhoneGap 2.0 开发
PhoneGap 2.0 开发
 
Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发Android 平台 HTML5 应用开发
Android 平台 HTML5 应用开发
 
HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型HTML5 生态系统和应用架构模型
HTML5 生态系统和应用架构模型
 
18 NSUserDefaults
18 NSUserDefaults18 NSUserDefaults
18 NSUserDefaults
 
17 Localization
17 Localization17 Localization
17 Localization
 

12 Camera

  • 2. • 在这个主题我们将给 Homepwner 增加照⽚片 • 我们将呈现⼀一个 UIImagePickerController,这样⽤用 户就可以获取并保存每个 item 的照⽚片。 • 这个图⽚片会被关联到⼀一个 BNRItem 实例,存储到 ⼀一个 image store 中,并且在 item 的详细视图中可 以看到
  • 4. 图⽚片显⽰示和 UIImageView • 要显⽰示图⽚片,⼀一个简单的⽅方法就是把⼀一个 UIImageView 放到屏幕上。打开 Hompwner 项 ⺫⽬目,和 DetailViewController.xib,拖拽⼀一个 UIImageView 到 view 上
  • 5. contentMode - Aspect Fit • UIImageView 根据它的 contentMode 属性来显⽰示图 ⽚片,这个属性决定了如何定位以及如何在它的 frame 内调整内容的⼤大⼩小。 • contentMode 默认值是 UIViewContentModeScaleToFill,它将调整图⽚片来 正好匹配 image view 的 bounds。为了不使图⽚片变 形,我们把它改成 “Aspect Fit”
  • 6. 连接到 view controller • 控件设置好了以后,我们⽤用前⾯面提到的从 UIImageView Control-drag 到 DetailViewController.h 的实例变量区域的⽅方法,⽣生 成⼀一个名为 imageView 的 outlet,选择 Weak 作为 存储类型 @interface DetailViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate> { __weak IBOutlet UITextField *nameField; __weak IBOutlet UITextField *serialNumberField; __weak IBOutlet UILabel *dateLabel; __weak IBOutlet UITextField *valueField; __weak IBOutlet UIImageView *imageView; }
  • 8. • 我们需要⼀一个按钮来启动照⽚片获取的过程:在 DetailViewController.xib 中,拖动⼀一个 UIToolbar 到 DetailViewController 的 view 的底部 • UIToolbar 和 UINavigationBar 类似的是我们都可以 给它添加 UIBarButtonItems。 • 不同的是,导航栏针对 bar button item 只有两个 插槽,⽽而 toolbar 有⼀一个 bar button item 的数组, 只要屏幕放的下我们可以往⾥里⾯面添加尽量多的 UIBarButtonItem
  • 9. Identifier -> Camera • 默认情况下,在 XIB ⽂文件中新⽣生成的 UIToolbar 实 例会带⼀一个 UIBarButtonItem。 • 选中这个 bar button item,打开 attribute inspector,把它的 Identifier 改成 Camera,这个 item 就会显⽰示⼀一个 camera 图标
  • 10. 带 bar button item 的 UIToolbar
  • 11. camera 按钮⽅方法 • 有了按钮以后,我们要在代码中声明它触发的⽅方 法 • 选中这个 camera 按钮,然后从按钮 Control-drag 到 DetailViewController.h 中⽅方法声明的区域 • 选择 Action,⽅方法命名为:takePicture: • 这样 camera 按钮按下时就会发送这个消息给 DetailViewController。 • ⽤用这种⽅方式连接⼀一个 action ⽅方法,同时会⾃自动在 DetailViewController.m 中添加⼀一个 stub implementation
  • 12. UIImagePickerController • camera 按钮的⽅方法有了之后,我们就得看⼀一下怎 么在这个 takePicture: ⽅方法⾥里⾯面实现拍照(或选取 照⽚片) • 我们可以使⽤用 UIImagePickerController,调⽤用系统 本⾝身的应⽤用来获取照⽚片。 • 当创建这个类的实例时,我们必须指定它的 sourceType 属性,并且分配给它⼀一个 delegate
  • 13. sourceType 属性 • sourceType 是⼀一个⽤用来告诉 image picker 到哪⾥里 获得照⽚片的常量: • UIImagePickerControllerSourceTypeCamera - 拍新照 ⽚片 • UIImagePickerControllerSourceTypeSavedPhotoAlbu m 和 UIImagePickerControllerSourceTypePhotoLibrary 都是从保存的照⽚片中选取
  • 14. • 在开始使⽤用 UIImagePickerControllerSourceTypeCamera 之前要先 判断设备有没有摄像头。 • ⽅方法是以 sourceType 常量作为参数发送 isSourceTypeAvailable: 消息给 UIImagePickerController
  • 15. delegate 和消息 • 除了 sourceType 之外,UIImagePickerController 的 实例还需要⼀一个 delegate 来处理来⾃自它的 view 的 请求。 • 当⽤用户在 UIImagePickerController 界⾯面确认选取的 图⽚片后, imagePickerController:didFinishPickingMediaWithInfo : 消息被发送给它的 delegate。(如果这个过程被 cancel 掉的话,消息是: imagePickerControllerDidCancel:) • 在 takePicker: 中增加初始化和设置 delegate 的代 码
  • 16. imagePicker 的初始化和设置 - (IBAction)takePicture:(id)sender { UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; // 如果设备有 camera 的话,我们就采集⼀一个图⽚片,否则我们从 photo library 从拾取⼀一 个 if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { [imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera]; } else { [imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; } [imagePicker setDelegate:self]; } • 初始化和设置完成之后,下⼀一步就是要把它显⽰示 出来。imagePicker 和之前⽤用到的 UIViewController ⼦子类不同的是它要模态化呈现。 • 模态化:占满全屏,直到完成⼯工作。
  • 17. 显⽰示 imagePicker • 要模态化的呈现⼀一个视图,发送 presentViewController:animated:completion: 消 息给当前其 view 在屏幕上显⽰示的 UIViewController。 • 传递的第⼀一个参数就是要呈现的 view controller, 它的 view 会从屏幕底部向上滑动出来 • 在 takePicture: 末尾增加显⽰示 imagePicker 的代码 [imagePicker setDelegate:self]; // 把 image picker 放到屏幕上(模态对话框) [self presentViewController:imagePicker animated:YES completion:nil]; }
  • 18. UINavigationControllerDelegate • imagePicker 的初始化,设置和显⽰示都完成了,下 ⼀一步就是获取 imagePicker 选择的图⽚片 • 需要在 DetailViewController 中实现我们前⾯面提到 的 imagePickerController:didFinishPickingMediaWithInfo : • UIImagePickerController 是 UINavigationController 的⼦子类,在类声明中添加下⻚页这些 protocol
  • 19. image 的独⽴立存储 @interface DetailViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate> { • 我们可以直接在didFinishPickingMediaWithInfo: 中 直接把选择的 image 设置给 imageView • 但是我们最好为 image 创建⼀一个单独的存储。
  • 20. • 我们把图⽚片放到 store 中,这样当 DetailViewController 的 view 下次在屏幕上显⽰示 时,我们让 DetailViewController 从 image store 中 提取图⽚片,然后放到它⾃自⼰己的 imageView • ⼀一般来讲,每次⼀一个 view controller 收到 viewWillAppear: 消息时都应该重新使⽤用数据填充 它的视图的⼦子视图
  • 22. • 这样我们在真正显⽰示选中的图⽚片前,先创建⼀一个 store 对它进⾏行管理。 • image store 将会持有⽤用户会⽤用到的全部图⽚片。 • ⽣生成⼀一个名为 BNRImageStore 的 NSObject 的⼦子类 • ⽣生成下列接⼝口: @interface BNRImageStore : NSObject { NSMutableDictionary *dictionary; } + (BNRImageStore *)sharedStore; - (void)setImage:(UIImage *)i forKey:(NSString *)s; - (UIImage *)imageForKey:(NSString *)s; - (void)deleteImageForkey:(NSString *)s; @end
  • 23. 实现 Singleton • 和 BNRItemStore ⼀一样,BNRImageStore 也需要是 单例的。在 BNRImageStore.m 中编写下⾯面这些和 BNRItemStore 类似的代码 + (id)allocWithZone:(NSZone *)zone { return [self sharedStore]; } + (BNRImageStore *)sharedStore { static BNRImageStore *sharedStore = nil; if (!sharedStore) { sharedStore = [[super allocWithZone:NULL] init]; } return sharedStore; } - (id)init { self = [super init]; if (self) { dictionary = [[NSMutableDictionary alloc] init]; } return self; }
  • 24. 实现头⽂文件中声明的另外三个⽅方法 - (void)setImage:(UIImage *)i forKey:(NSString *)s { [dictionary setObject:i forKey:s]; } - (UIImage *)imageForKey:(NSString *)s { return [dictionary objectForKey:s]; } - (void)deleteImageForkey:(NSString *)s { if (!s) { return; } [dictionary removeObjectForKey:s]; } • setImage: 增加(更新) • imageForKey: 获取 • deleteImageForKey: 删除
  • 25. NSMutableDictionary • 经过上⾯面⼀一些操作,基本的 image store 功能就完 成了。 • 这⾥里⾯面我们使⽤用的是 NSMutableDictionary 来存 储数据(key-value pair) • key 是⼀一个唯⼀一值(⼀一般是字符串),value 是被 存储在集合中的对象
  • 28. • 要使⽤用这个 image store,⾸首先我们要想办法能够 ⽣生成唯⼀一的 key 。 • ⼀一个是因为在把图⽚片存到字典时要⽤用到; • 另外要把这个 key 给相关联的 BNRItem,在 DetailViewController 需要从 image store 拿到⼀一张图⽚片 时,要询问它的 item key 是多少,然后使⽤用这个 key 在字典中搜索图⽚片 • ⾸首先给 BNRItem 增加⼀一个属性来存储这个 key @property (nonatomic, readonly, strong) NSDate *dateCreated; @property (nonatomic, copy) NSString *imageKey; @implementation BNRItem @synthesize imageKey;
  • 29. CFUUIDRef 和 CFUUIDCreate • 要保证 key 的唯⼀一性,可以使⽤用 UUID 的⽅方式 • CFUUIDRef 类型的对象表⽰示⼀一个 UUID,下⾯面在 imagePickerController:didFinishPickingMediaWithInfo : 中⽣生成⼀一个 UUID UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; // ⽣生成⼀一个 CFUUID 对象 CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault); • CF对象的创建通过调⽤用以被创建的对象的类型开 始,并且包含“Create”的函数(CFUUIDCreate) • 指定的第⼀一个参数是内存如何分配,传⼊入 kCFAllocateDefault 表⽰示由系统决定
  • 30. CFUUIDCreateString 和 CFStringRef • CFUUIDRef 是⼀一个字节数组(15个uint8) • 因为我们要把 UUID 作为字典的 key,以及在后⾯面 存储到⽂文件系统的时候会把它作为⽂文件名,所以 需要把 UUID 转成字符串 • 可以通过调⽤用 CFUUIDCreateString 从 CFUUIDRef ⽣生成⼀一个字符串对象 // ⽣生成⼀一个 CFUUID 对象 CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault); // 从 unique identifier ⽣生成⼀一个字符串 CFStringRef newUniqueIDString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueID);
  • 31. 类型转换 • CFStringRef 和 NSString 需要相互转换 • 转换类型,把它设置成选中的 BNRItem 的 imageKey,同时把 image 放到 BNRImageStore 中 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault); CFStringRef newUniqueIDString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueID); // CFStringRef -> NSString // 使⽤用这个 unique ID 来设置 item 的 imageKey NSString *key = (__bridge NSString *)newUniqueIDString; [item setImageKey:key]; // 使⽤用这个 key 把图⽚片保存到 BNRImageStore [[BNRImageStore sharedStore] setImage:image forKey:[item imageKey]];
  • 32. Core Foundation 和 toll-free bridging
  • 33. • 当⼀一个指向 Objective-C 对象的变量被销毁时, ARC 知道这个对象失去了⼀一个所有者。 • 但是 ARC 并不对 Core Foundation 的对象做这些 • 这样当 Core Foundation 的对象失去指针时,我们 必须在丢掉这个指针前调⽤用⼀一个函数来告诉对象 丢掉⼀一个所有者
  • 34. • 如果我们没有在失去指针前调⽤用 CFRelease ,被指 向的对象仍然认为它有⼀一个所有者 • 在告诉它丢掉⼀一个所有者之前丢掉指针会引起内 存泄露:对象已经访问不了了,但还有所有者
  • 35. CFRelease • 增加代码,告诉被 newUniqueIDString 和 newUniqueID 指向的对象丢掉⼀一个所有者,因为 都是局部变量,⽅方法结束将销毁 [[BNRImageStore sharedStore] setImage:image forKey:[item imageKey]]; // lose owner CFRelease(newUniqueIDString); CFRelease(newUniqueID); // 把图⽚片放到我们屏幕上的 image view 中 [imageView setImage:image];
  • 36. __bridge NSString *key = (__bridge NSString *)newUniqueIDString; • ARC 并不很会管理 Core Foundation 对象的内存 • 在我们类型转换⼀一个 Core Foundation 指针到 它的 Objective-C 同级形式时,放⼀一个 __bridge 在调⽤用 前⾯面,是告诉 ARC 不要像通常做的那样给 key 变 量⼀一个所有权
  • 37. 结束 BNRImageStore • 现在 BNRItemStore 可以存储图⽚片,并且 BNRItem 有获得这个图⽚片的 key,我们需要告诉 DetailViewController 怎样从选中的 BNRItem 抓取 图⽚片,并放⼊入它的 imageView
  • 38. Cache
  • 39. 获取并显⽰示照⽚片 • DetailViewController 的 view 将会在两个时间上出 现:当⽤用户轻击 ItemViewController 中的⼀一⾏行时, 当 UIImagePickerController 被释放时 • 在两种情况下,imageView 都应该被正在显⽰示的 BNRItem 的图⽚片填充。在 DetailViewController.m 中增加代码到 viewWillAppear: // 显⽰示图⽚片 NSString *imageKey = [item imageKey]; if (imageKey) { UIImage *imageToDisplay = [[BNRImageStore sharedStore] imageForKey:imageKey]; [imageView setImage:imageToDisplay]; } else { [imageView setImage:nil]; }
  • 40. 删除旧图⽚片 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { // 如果存在旧图⽚片,先把它从 BNRImageStore 中删除 NSString *oldKey = [item imageKey]; if (oldKey) { [[BNRImageStore sharedStore] deleteImageForkey:oldKey]; } // 从 info 字典中获得拾取的图⽚片 UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
  • 42. • 让 DetailViewController 符合 UITextFieldDelegate protocol,实现 textFieldShouldReturn: ⽅方法来让⽂文 本框字段在换⾏行键被按下时释放它的 first responder 状态以释放键盘 @interface DetailViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate> - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return YES; }
  • 43. UIControll 和 Touch Up Inside 事件 • 我们希望⽤用户在 DetailViewController 的 view 上的 任何地⽅方轻击都释放键盘,我们可以通过给 view 发送 endEditing: 消息实现释放键盘 • UIButton 可以在轻击时发送⼀一个 action 消息给它 的 target,这种 target-action 特性是从 UIControl 继承的
  • 44. • 在 DetailViewController.xib 中选择 View 对象,在 identity inspector 中把 view 的类改成 UIControl • 然后从 view Control-drag 到⽅方法声明区域,事件 选择 ”Touch Up Inside“
  • 45. - (IBAction)backgroundTapped:(id)sender { [[self view] endEditing:YES]; }