UIImageView
用UIImageView来显示图片,其contentMode属性控制图片如何显示,默认值是UIViewContentModeScaleToFill。
在XIB文件中添加image view,并在其File's owner中生成IBOutlet,修改image view的Mode attribute为Aspect Fit。
UIToolbar
类似UINavigationBar,也可以在UIToolbar中添加UIBarButtonItem,不同之处是,navigation bar中只能加两个,而tool bar中有个UIBarButtonItem数组,可以添加尽量多只要能在屏幕上显示。
在XIB中,一个UIToolBar实例默认带有一个UIBarButtonItem,选择这个button,在attribute inspector中,修改identifier为Camera,会显示一个camera icon。
camera button需要一个target和action,之前我们连接一个action方法分两步:在代码中声明action方法,然后通过在XIB中将button和action方法连接起来。不过,就像outlet,也可以通过assistant editor(通过Option-click打开),来快速创建action并连接起来,Control-drag button到File's owner的实现部分。
UIImagePickerController
选择图片,使用UIImagePickerController,需要设置其sourceType属性,并为其指定delegate。
sourceType告诉image picker去哪里获得图片:
sourceType constant | description |
---|---|
UIImagePickerControllerSourceTypeCamera | 通过相机拍摄 |
UIImagePickerControllerSourceTypePhotoLibrary | 显示相册,从某个相册选图片 |
UIImagePickerControllerSourceTypeSavedPhotosAlbum | 最近拍摄的图片 |
在使用相机拍摄前,要先判断设备是否支持相机。
- (IBAction)takePicture:(id)sender {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
// 判断设备是否支持相机拍摄
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
} else {
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
}
设置UIImagePickerController 的代理
当选择一张图片后,UIImagePickerController的代理会收到imagePickerController:didFinishPickingMediaWithInfo:消息,如果选择cancel,没有选择图片,则代理会收到imagePickerControllerDidCancel:消息。
设置image picker的代理为BKDetailViewController,在BKDetailViewController.m中声明实现UINavigationControllerDelegate 和 UIImagePickerControllerDelegate protocols
@interface BKDetailViewController ()
为什么需要UINavigationControllerDelegate?UIImagePickerController继承自其父类UINavigationController,BKDetailViewController要被设置为UIImagePickerController的delegate,所以不仅要实现UIImagePickerControllerDelegate protocol,还要实现其父类的代理协议-UINavigationBarDelegate。
- (IBAction)takePicture:(id)sender {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
// 判断设备是否支持相机拍摄
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
} else {
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
// 设置代理
imagePicker.delegate = self;
}
显示image picker
为UIImagePickerController设置好sourceType和delegate后,是时候将UIImagePickerController的view显示到屏幕上。
Unlike other UIViewController subclasses you have used, an instance of UIImagePickerController is presented modally.
To present a view controller modally, you send presentViewController:animated:completion: to the UIViewController whose view is on the screen. The view controller to be presented is passed to it, and this view controller’s view slides up from the bottom of the screen.
给view controller发送**presentViewController:animated:completion: **消息,并传递image picker controller,image picker controller的view会从屏幕下方滑动上来显示。
- (IBAction)takePicture:(id)sender {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
// 判断设备是否支持相机拍摄
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
} else {
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
// 设置代理
imagePicker.delegate = self;
// 显示 image picker controller's view
[self presentViewController:imagePicker animated:YES completion:nil];
}
获取选中的图片
前面提到了,当选中了图片后,代理会收到imagePickerController:didFinishPickingMediaWithInfo:消息,为代理添加此方法实现:
// image picker选中图片后,其代理收到此消息
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
// 获得图片
UIImage *image = info[UIImagePickerControllerOriginalImage];
self.imageView.image = image;
// 移除image picker
[self dismissViewControllerAnimated:YES completion:nil];
}
创建单例类来保存和查找图片
头文件
#import
@interface BKImageStore : NSObject
// 静态方法,获得单例
+ (instancetype)sharedStore;
// 实例方法
- (void)setImage:(UIImage *)image forKey:(NSString *)key;
- (UIImage *)imageForKey:(NSString *)key;
- (void)deleteImageForKey:(NSString *)key;
@end
实现文件
#import "BKImageStore.h"
@interface BKImageStore()
@property (nonatomic, strong) NSMutableDictionary *dictionary;
@end
@implementation BKImageStore
// 静态方法,调用此该来获取单例实例
+ (instancetype)sharedStore{
static BKImageStore *sharedStore = nil;
if(!sharedStore){
sharedStore = [[self alloc] initPrivate];
}
return sharedStore;
}
// 如果调用此方法直接抛出错误
- (instancetype)init{
@throw [NSException exceptionWithName:@"Singleton" reason:@"Use +[BKImageStore sharedStore]" userInfo:nil];
return nil;
}
// 私有的初始化方法
- (instancetype)initPrivate{
self = [super init];
if (self) {
_dictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
// 设置指定KEY的对象
- (void)setImage:(UIImage *)image forKey:(NSString *)key{
//[self.dictionary setObject:image forKey:key];
self.dictionary[key] = image;
}
// 获取指定KEY的对象
- (UIImage *)imageForKey:(NSString *)key{
//return [self.dictionary objectForKey:key];
return self.dictionary[key];
}
// 删除指定KEY的对象
- (void)deleteImageForKey:(NSString *)key{
if (!key) {
return;
}
[self.dictionary removeObjectForKey:key];
}
@end
Dictionary
dictionary,可以通过快速语法(shorthand syntax)创建:
NSDictionary *dictionary = @{@"key": object, @"anotherKey": anotherObject};
获取某个key的对象:
id object = dictionary[@"key"];
// same as
id object = [dictionary objectForKey:@"key"];
设置某个key的对象:
dictionary[@"key"] = object;
// same as
[dictionary setObject:object forKey:@"key"];
用UUID生成key
当添加图片到dictionary中,需要有一个唯一KEY,并把这个KEY设置给BKItem对象。
唯一KEY可以使用UUID,通过NSUUID来生成。
在BKItem.h中声明一属性来保存key:
@property (nonatomic, copy) NSString *itemKey;
在BKItem.m的初始化方法中生成UUID:
// Designated initializer (指定的构造器,通常是参数最多的那个init方法)
- (instancetype)initWithItemName:(NSString *)name valueInDollars:(int) value serialNumber:(NSString *)sNumber{
// Call the superclass's designated initializer
self = [super init];
// Did the superclass's designated initializer succeed?
if (self) {
// Give the instance variables initial values
_itemName = name;
_serialNumber = sNumber;
_valueInDollars = value;
// Set _dateCreated to the current date and time
_dateCreated = [[NSDate alloc] init];
// 创建UUID
NSUUID *uuid = [[NSUUID alloc] init];
NSString *key = [uuid UUIDString];
_itemKey = key;
}
// Return the address of the newly initialized object
return self;
}
当image picker选中图片后,将图片保存到dictionary中
// image picker选中图片后,其代理收到此消息
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
// 获得图片
UIImage *image = info[UIImagePickerControllerOriginalImage];
// 保存图片到dictionary
[[BKImageStore sharedStore] setImage:image forKey:self.item.itemKey];
self.imageView.image = image;
// 移除image picker
[self dismissViewControllerAnimated:YES completion:nil];
}
在BKItemStore.m文件中,当删除BKItem时,删除与之相关的image:
#import "BKImageStore.h"
- (void)removeItem:(BKItem *)item{
// 删除对应的图片
NSString *key = item.itemKey;
[[BKImageStore sharedStore] deleteImageForKey:key];
//[self.privateItems removeObject:item];
[self.privateItems removeObjectIdenticalTo:item];
}
在BKDetailViewController.m中,在viewWillAppear:方法中,查找BKItem对应的image,设置给image view.
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
BKItem *item = self.item;
self.nameField.text = item.itemName;
self.serialField.text = item.serialNumber;
self.valueField.text = [NSString stringWithFormat:@"%d", item.valueInDollars];
static NSDateFormatter *dateFormatter = nil;
if(!dateFormatter){
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateStyle = NSDateFormatterMediumStyle;
dateFormatter.timeStyle = NSDateFormatterNoStyle;
}
self.dateLabel.text = [dateFormatter stringFromDate:item.dateCreated];
// 根据BKItem中的key查找图片
NSString *imageKey = self.item.itemKey;
UIImage *imageToDisplay = [[BKImageStore sharedStore] imageForKey:imageKey];
// 当imageToDisplay为nil,UIImageView不会显示图片
self.imageView.image = imageToDisplay;
}
Dismissing the keyboard
要隐藏键盘,需要实现UITextFieldDelegate protocol,并实现textFieldShouldReturn:方法,这样当点击return键时,键盘会隐藏。
// 当点击return键 隐藏键盘
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
还应该添加更人性化的方法,当点击BKDetailViewController's view的任何地方,隐藏键盘
You have seen how classes like UIButton can send an action message to a target when tapped. Buttons inherit this target-action behavior from their superclass, UIControl. You are going to change the view of BKDetailViewController from an instance of UIView to an instance of UIControl so that it can handle touch events.
UIButton是UIControl的子类,UIControl都有target-action行为,当点击UIControl,会发送action消息到target。
修改BKDetailViewController的view,将其他class指定为UIControl:
然后在assistant editor中打开BKDetailViewController.m,Control-drag view到view controller的实现:
注意上面的GIF中有个错误,在关联action时,在弹出框中要选择event为Touch Up Inside.
要让视图隐藏键盘,需要调用endEditing:方法:
- (IBAction)backgroundTapped:(id)sender {
// 隐藏键盘
[self.view endEditing:YES];
}
本文是对《iOS Programming The Big Nerd Ranch Guide 4th Edition》第十一章的总结。