代理、通知、KVO的应用
实现下图效果,每点击一次cell的“加号”或者“减号”,就可以让“底部view”的总价进行对应的增加或者减少。
下图是实际运行效果图:
图(1)
因为“底部UIView”需要一直显示在底部。如果把底部UIView添加到tableView上会导致其跟随tableView的滚动而滚动,所以不能把底部UIView作为tableView的子控件。所以把底部UIView添加到控制器的view上,让底部的UIView和tableView同级。如下图2展示了视图层级结构:
图(1)
方案一:KVO实现:
控制器监听数据模型中count属性的变化,进而根据count的增减做出不同的操作
步骤:
1.添加KVO监听
2.监听到键值变化后的操作
3.移除KVO监听
#import "ViewController.h"
#import "XMGWineCell.h"
#import "XMGWine.h"
#import "MJExtension.h" @interface ViewController () <UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UIButton *buyButton;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
/** 酒数据 */
@property (nonatomic, strong) NSArray *wineArray;
/** 总价 */
@property (weak, nonatomic) IBOutlet UILabel *totalPriceLabel;
@end @implementation ViewController
- (NSArray *)wineArray
{
if (!_wineArray) {
self.wineArray = [XMGWine objectArrayWithFilename:@"wine.plist"]; for (XMGWine *wine in self.wineArray) {
// 添加KVO监听
[wine addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}
}
return _wineArray;
} - (void)viewDidLoad {
[super viewDidLoad]; }
#pragma mark - 移除KVO监听
- (void)dealloc
{
for (XMGWine *wine in self.wineArray) {
[wine removeObserver:self forKeyPath:@"count"];
}
} #pragma mark - KVO监听
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(XMGWine *)wine change:(NSDictionary *)change context:(void *)context
{
// NSKeyValueChangeNewKey == @"new"
int new = [change[NSKeyValueChangeNewKey] intValue];
// NSKeyValueChangeOldKey == @"old"
int old = [change[NSKeyValueChangeOldKey] intValue]; if (new > old) { // 增加数量
int totalPrice = self.totalPriceLabel.text.intValue + wine.money.intValue;
self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
self.buyButton.enabled = YES;
} else { // 数量减小
int totalPrice = self.totalPriceLabel.text.intValue - wine.money.intValue;
self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
self.buyButton.enabled = totalPrice > ;
}
}
方案二:通知:
步骤:
1.注册通知
2.添加监听
3.移除通知
1.在自定义cell的.m文件中注册通知:
#import "XMGWineCell.h"
#import "XMGWine.h"
#import "XMGCircleButton.h" @interface XMGWineCell()
@property (weak, nonatomic) IBOutlet XMGCircleButton *minusButton;
@property (weak, nonatomic) IBOutlet UIImageView *imageImageView;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UILabel *moneyLabel;
@property (weak, nonatomic) IBOutlet UILabel *countLabel;
@end @implementation XMGWineCell - (void)setWine:(XMGWine *)wine
{
_wine = wine; self.imageImageView.image = [UIImage imageNamed:wine.image];
self.nameLabel.text = wine.name;
self.moneyLabel.text = wine.money;
self.countLabel.text = [NSString stringWithFormat:@"%d", wine.count];
self.minusButton.enabled = (wine.count > );
} /**
* 加号点击
*/
- (IBAction)plusClick {
// 修改模型
self.wine.count++; // 修改数量label
self.countLabel.text = [NSString stringWithFormat:@"%d", self.wine.count]; // 减号能点击
self.minusButton.enabled = YES; // 发布通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"plusClickNotification" object:self];
} /**
* 减号点击
*/
- (IBAction)minusClick {
// 修改模型
self.wine.count--; // 修改数量label
self.countLabel.text = [NSString stringWithFormat:@"%d", self.wine.count]; // 减号按钮不能点击
if (self.wine.count == ) {
self.minusButton.enabled = NO;
} // 发布通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"minusClickNotification" object:self];
}
@end
2.在控制器的.m文件中添加监听(在通知中心注册监听)
3.在控制器的dealloc方法中移除监听
#import "ViewController.h"
#import "XMGWineCell.h"
#import "XMGWine.h"
#import "MJExtension.h" @interface ViewController () <UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UIButton *buyButton;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
/** 酒数据 */
@property (nonatomic, strong) NSArray *wineArray;
/** 总价 */
@property (weak, nonatomic) IBOutlet UILabel *totalPriceLabel;
@end @implementation ViewController
- (NSArray *)wineArray
{
if (!_wineArray) {
self.wineArray = [XMGWine objectArrayWithFilename:@"wine.plist"];
}
return _wineArray;
} - (void)viewDidLoad {
[super viewDidLoad]; // 监听通知
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(plusClick:) name:@"plusClickNotification" object:nil];
[center addObserver:self selector:@selector(minusClick:) name:@"minusClickNotification" object:nil];
} - (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
} #pragma mark - 按钮点击
- (IBAction)clear {
self.totalPriceLabel.text = @""; // 将模型里面的count清零
for (XMGWine *wine in self.wineArray) {
wine.count = ;
} // 刷新
[self.tableView reloadData]; self.buyButton.enabled = NO;
} - (IBAction)buy {
// 打印出所有要买的东西
for (XMGWine *wine in self.wineArray) {
if (wine.count) {
NSLog(@"%d件【%@】", wine.count, wine.name);
}
}
} #pragma mark - 监听通知
- (void)plusClick:(NSNotification *)note
{
self.buyButton.enabled = YES; // 取出cell(通知的发布者)
XMGWineCell *cell = note.object;
// 计算总价
int totalPrice = self.totalPriceLabel.text.intValue + cell.wine.money.intValue;
// 设置总价
self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
} - (void)minusClick:(NSNotification *)note
{
// 取出cell(通知的发布者)
XMGWineCell *cell = note.object;
// 计算总价
int totalPrice = self.totalPriceLabel.text.intValue - cell.wine.money.intValue;
// 设置总价
self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice]; self.buyButton.enabled = totalPrice > ;
}
方案三:delegate:
步骤:
1.制定代理协议
2.设置代理属性
3.遵守协议,实现代理方法
4.调用代理实现的协议方法
1.制定代理协议
2.设置代理属性
#import <UIKit/UIKit.h>
@class XMGWine, XMGCircleButton, XMGWineCell; @protocol XMGWineCellDelegate <NSObject>
@optional
- (void)wineCellDidClickPlusButton:(XMGWineCell *)wineCell;
- (void)wineCellDidClickMinusButton:(XMGWineCell *)wineCell;
@end @interface XMGWineCell : UITableViewCell
@property (strong, nonatomic) XMGWine *wine; /** 代理对象 */
@property (nonatomic, weak) id<XMGWineCellDelegate> delegate;
@end
自定义cell的.m文件中判断代理是否响应协议方法,如果响应,则调用代理实现的协议方法。
/**
* 加号点击
*/
- (IBAction)plusClick {
// 修改模型
self.wine.count++; // 修改数量label
self.countLabel.text = [NSString stringWithFormat:@"%d", self.wine.count]; // 减号能点击
self.minusButton.enabled = YES; // 通知代理(调用代理的方法)
// respondsToSelector:能判断某个对象是否实现了某个方法
if ([self.delegate respondsToSelector:@selector(wineCellDidClickPlusButton:)]) {
[self.delegate wineCellDidClickPlusButton:self];
}
} /**
* 减号点击
*/
- (IBAction)minusClick {
// 修改模型
self.wine.count--; // 修改数量label
self.countLabel.text = [NSString stringWithFormat:@"%d", self.wine.count]; // 减号按钮不能点击
if (self.wine.count == ) {
self.minusButton.enabled = NO;
} // 通知代理(调用代理的方法)
if ([self.delegate respondsToSelector:@selector(wineCellDidClickMinusButton:)]) {
[self.delegate wineCellDidClickMinusButton:self];
}
}
3.控制器的.m文件中遵守代理协议,实现协议方法
#import "ViewController.h"
#import "XMGWineCell.h"
#import "XMGWine.h"
#import "MJExtension.h" @interface ViewController () <UITableViewDataSource, XMGWineCellDelegate, UITableViewDelegate>
@property (weak, nonatomic) IBOutlet UIButton *buyButton;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
/** 酒数据 */
@property (nonatomic, strong) NSArray *wineArray;
/** 总价 */
@property (weak, nonatomic) IBOutlet UILabel *totalPriceLabel; /** 购物车对象(存放需要购买的商品) */
@property (nonatomic, strong) NSMutableArray *wineCar;
@end @implementation ViewController
- (NSMutableArray *)wineCar
{
if (!_wineCar) {
_wineCar = [NSMutableArray array];
}
return _wineCar;
} - (NSArray *)wineArray
{
if (!_wineArray) {
self.wineArray = [XMGWine objectArrayWithFilename:@"wine.plist"];
}
return _wineArray;
} - (void)viewDidLoad {
[super viewDidLoad]; } #pragma mark - <XMGWineCellDelegate>
- (void)wineCellDidClickMinusButton:(XMGWineCell *)wineCell
{
// 计算总价
int totalPrice = self.totalPriceLabel.text.intValue - wineCell.wine.money.intValue; // 设置总价
self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice]; self.buyButton.enabled = totalPrice > ; // 将商品从购物车中移除
if (wineCell.wine.count == ) {
[self.wineCar removeObject:wineCell.wine];
}
} - (void)wineCellDidClickPlusButton:(XMGWineCell *)wineCell
{
// 计算总价
int totalPrice = self.totalPriceLabel.text.intValue + wineCell.wine.money.intValue; // 设置总价
self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice]; self.buyButton.enabled = YES; // 如果这个商品已经在购物车中,就不用再添加
if ([self.wineCar containsObject:wineCell.wine]) return; // 添加需要购买的商品
[self.wineCar addObject:wineCell.wine];
}
代理、通知、KVO的应用的更多相关文章
- iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车
一.通知 1.通知中心(NSNotificationCenter)每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信任何一个对象都可以向 ...
- ios通知-kvo
// KVC: Key Value Coding, 常见作用:给模型属性赋值 // KVO: Key Value Observing, 常用作用:监听模型属性值的改变 // // ViewCon ...
- [ 单例、代理 & 通知 ]
PS:手写单例.代理方法实现 & 通知的简单使用! [ 单例模式,代理设计模式,观察者模式! ] 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设 ...
- IOS第十天(1:QQ好友列表 ,自定义的headview,代理 ,通知 ,black的使用)
*****HMViewController.m #import "HMViewController.h" #import "HMFriendsGroupModel.h&q ...
- 使用注解配置Spring框架自动代理通知
话不多说上代码 项目架构图及Lib包如下: 第二步创建业务类接口 package cn.happy.day01.entity; /** * 1.业务接口 * @author Happy * */ pu ...
- Block实现代理/通知效果
例子1:A控制器->跳转—>B控制器 , 假设想从B控制器回传数组给A控制器 实现:B控制器.h文件定义一个block参数,.m文件执行block,A控制器设置block内容 B.h文件/ ...
- 线程间通信 GET POST
线程间通信有三种方法:NSThread GCD NSOperation 进程:操作系统里面每一个app就是一个进程. 一个进程里面可以包含多个线程,并且我们每一个app里面有且仅有一 ...
- OC 观察者模式(通知中心,KVO)
OC 观察者模式(通知中心,KVO) 什么是观察者模式??? A对B的变化感兴趣,就注册为B的观察者,当B发生变化时通知A,告知B发生了变化.这就是观察者模式. 观察者模式定义了一种一对多的依赖关系, ...
- IOS第二天-新浪微博 - 添加搜索框,弹出下拉菜单 ,代理的使用 ,HWTabBar.h(自定义TabBar)
********HWDiscoverViewController.m(发现) - (void)viewDidLoad { [super viewDidLoad]; // 创建搜索框对象 HWSearc ...
- iOS监听模式之KVO、KVC的高阶应用
KVC, KVO作为一种魔法贯穿日常Cocoa开发,笔者原先是准备写一篇对其的全面总结,可网络上对其的表面介绍已经够多了,除去基本层面的使用,笔者跟大家谈下平常在网络上没有提及的KVC, KVO进阶知 ...
随机推荐
- poj3278 Catch That Cow
Catch That Cow Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 73973 Accepted: 23308 ...
- 图片上传功能<转>http://blog.csdn.net/u011159417/article/details/50126023
以前也实现过上传,只不过每次都是,写完之后没有总结,下次遇到时,还要重新写,重新调式,很是浪费时间,所以,今天实现一个上传图片的功能,包括简单的页面和servlet,下次再要写这个功能时,直接拿过来就 ...
- Splay整理
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.(来自百科) 伸展树的操作主要是
- sublime配置文件
起初是为了解决 tab转四个空格问题 安装包 Sublime_Text_Build_3103_x64_CHS_Lfqy.exe 配置方法 配置脚本 { "bold_folder_labels ...
- Activity中获取view的高度和宽度为0的原因以及解决方案
在activity中可以调用View.getWidth.View.getHeight().View.getMeasuredWidth() .View.getgetMeasuredHeight()来获得 ...
- java之yield(),sleep(),wait()区别详解
1.sleep() 使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁.也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据.注意该方 ...
- css3实践之摩天轮式图片轮播+3D正方体+3D标签云(perspective、transform-style、perspective-origin)
本文主要通过摩天轮式图片轮播的例子来讲解与css3 3D有关的一些属性. demo预览: 摩天轮式图片轮播(貌似没兼容360 最好用chrome) 3D正方体(chrome only) 3D标签云(c ...
- 用canvas画简单的“我的世界”人物头像
前言:花了4天半终于看完了<Head First HTML5>,这本书的学习给我最大的感受就是,自己知识的浅薄,还有非常多非常棒的技术在等着我呢.[熊本表情]扶朕起来,朕还能学! H5新增 ...
- 深入理解计算机系统(2.4)---C语言的有符号与无符号、二进制整数的扩展与截断
开篇请各位猿友允许LZ啰嗦几句,最近一直在写计算机系统原理这系列文章,也已经下定决心要把这本书的内容写完.主要目的其实是为了巩固LZ的理解,另外也想把这些内容分享给猿友们,毕竟LZ觉得这些内容对程序猿 ...
- 备忘:maven 错误信息: Plugin execution not covered by lifecycle configuration
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...