UIImageView

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中声明实现UINavigationControllerDelegateUIImagePickerControllerDelegate protocols

@interface BKDetailViewController () <UINavigationBarDelegate, UIImagePickerControllerDelegate>

为什么需要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 <Foundation/Foundation.h>

@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》第十一章的总结。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容