您的位置:首页 > 产品设计 > UI/UE

IOS开发UI进阶之UITableView四

2015-10-06 20:23 351 查看

一 购物车

骨架搭建

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"wine";
WineCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

return cell;
}


设置按钮边框

方法一 抽取代码如下:

- (void)setupCircleButton:(UIButton *)button
{
// 设置边框颜色
button.layer.borderColor = [UIColor redColor].CGColor;
// 设置边框宽度
button.layer.borderWidth = 1;
// 设置圆角半径
button.layer.cornerRadius = button.frame.size.width * 0.5;
}

- (void)awakeFromNib {
[self setupCircleButton:self.minusButton];
[self setupCircleButton:self.plusButton];
}


自定义按钮CircleButton

- (void)awakeFromNib
{
// 设置边框颜色
self.layer.borderColor = [UIColor redColor].CGColor;
// 设置边框宽度
self.layer.borderWidth = 1;
// 设置圆角半径
self.layer.cornerRadius = self.frame.size.width * 0.5;
}


加减号处理逻辑

加号点击

- (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 == 0) {
self.minusButton.enabled = NO;
}

// 发布通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"minusClickNotification" object:self];
}


二 通知

通知中心(NSNotificationCenter)

每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信

任何一个对象都可以向通知中心发布通知(NSNotification),描述自己在做什么。其他对象(Observer)可以申请在某个特定通知发布时(或在某个特定的对象发布通知时)收到这个通知

通知(NSNotification)

一个完整的通知一般包含3个属性:

- (NSString *)name; // 通知的名称
- (id)object; // 通知发布者(是谁要发布通知)
- (NSDictionary *)userInfo; // 一些额外的信息(通知发布者传递给通知接收者的信息内容)


模拟通知

注册通知监听器

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
//  observer:监听器,即谁要接收这个通知
//  aSelector:收到通知后,回调监听器的这个方法,并且把通知对象当做参数传入
//  aName:通知的名称。如果为nil,那么无论通知的名称是什么,监听器都能收到这个通知
//  anObject:通知发布者。如果为anObject和aName都为nil,监听器都收到所有的通知


// 监听通知
[[NSNotificationCenter defaultCenter] addObserver:p1 selector:@selector(gotNews:) name:@"军事新闻" object:cmp1];
// 创建通知
NSNotification *note = [NSNotification notificationWithName:@"军事新闻" object:cmp1                                                                                                              userInfo:@{
@"title" : @"XXX国家好牛逼!",
@"desc" : @"其实也没有牛逼的!"                                                                                                                       }];
// 发布通知
[[NSNotificationCenter defaultCenter] postNotification:note];
[[NSNotificationCenter defaultCenter] postNotificationName:@"军事新闻" object:nil userInfo:@{@"title" : @"453543"}];


移除通知

[[NSNotificationCenter defaultCenter] removeObserver:p1];


三 UIDevice通知

UIDevice类提供了一个单粒对象,设备通过它可以获得一些设备相关的信息,比如电池电量值(batteryLevel)、电池状态(batteryState)、设备的类型(model,比如iPod、iPhone等)、设备的系统(systemVersion)

通过[UIDevice currentDevice]可以获取这个单粒对象

UIDevice对象会不间断地发布一些通知,下列是UIDevice对象所发布通知的名称常量:

UIDeviceOrientationDidChangeNotification // 设备旋转

UIDeviceBatteryStateDidChangeNotification // 电池状态改变

UIDeviceBatteryLevelDidChangeNotification // 电池电量改变

UIDeviceProximityStateDidChangeNotification // 近距离传感器(比如设备贴近了使用者的脸部)

键盘通知

键盘状态改变的时候,系统会发出一些特定的通知

UIKeyboardWillShowNotification // 键盘即将显示

UIKeyboardDidShowNotification // 键盘显示完毕

UIKeyboardWillHideNotification // 键盘即将隐藏

UIKeyboardDidHideNotification // 键盘隐藏完毕

UIKeyboardWillChangeFrameNotification // 键盘的位置尺寸即将发生改变

UIKeyboardDidChangeFrameNotification // 键盘的位置尺寸改变完毕

系统发出键盘通知时,会附带跟键盘有关的额外信息(字典),字典常见的key如下:

UIKeyboardFrameBeginUserInfoKey // 键盘刚开始的frame

UIKeyboardFrameEndUserInfoKey // 键盘最终的frame(动画执行完毕后)

UIKeyboardAnimationDurationUserInfoKey // 键盘动画的时间

UIKeyboardAnimationCurveUserInfoKey // 键盘动画的执行节奏(快慢)

四 购物车清空与购买(合计金额)

通知

- (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];
}


监听通知

- (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 > 0;
}


KVO

监听酒模型count数变化

- (NSArray *)wineArray
{
if (!_wineArray) {
self.wineArray = [XMGWine objectArrayWithFilename:@"wine.plist"];

for (XMGWine *wine in self.wineArray) {
[wine addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}
}
return _wineArray;
}


- (void)dealloc
{
for (XMGWine *wine in self.wineArray) {
[wine removeObserver:self forKeyPath:@"count"];
}
}


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 > 0;
}
}


代理设计模式

遵守cell代理协议WineCellDelegate(控件名+Delegate)

@protocol WineCellDelegate <NSObject>
@optional  //  这个方法不一定要实现
- (void)wineCellDidClickPlusButton:(WineCell *)winCell; //Did仿苹果系统
- (void)wineCellDidClickMinusButton:(WineCell *)winCell;
@end

/** 代理对象 */
@property (nonatomic, weak) id<WineCellDelegate> delegate;
// 用id可以减少耦合性


在ViewController中实现代理方法

调用代理方法

- (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];
}
}


五 iOS监听事件的方法

代理的使用步骤

定义一份代理协议

协议名字的格式一般是:类名 + Delegate

比如UITableViewDelegate

代理方法细节

一般都是@optional

方法名一般都以类名开头

比如
- (void)scrollViewDidScroll:


一般都需要将对象本身传出去

比如tableView的方法都会把tableView本身传出去

必须要遵守NSObject协议

比如
@protocol XMGWineCellDelegate <NSObject>


声明一个代理属性

代理的类型格式:id<协议> delegate

@property (nonatomic, weak) id<XMGWineCellDelegate> delegate;


设置代理对象

xxx.delegate = yyy;


代理对象遵守协议,实现协议里面相应的方法

当控件内部发生了一些事情,就可以调用代理的代理方法通知代理

如果代理方法是@optional,那么需要判断方法是否有实现

if ([self.delegate respondsToSelector:@selector(wineCellDidClickPlusButton:)]) {
[self.delegate wineCellDidClickPlusButton:self];
}


iOS监听某些事件的方法对比

通知(NSNotificationCenter\NSNotification)

任何对象之间都可以传递消息

使用范围

1个对象可以发通知给N个对象

1个对象可以接受N个对象发出的通知

必须得保证通知的名字在发出和监听时是一致的

KVO

仅仅是能监听对象属性的改变(灵活度不如通知和代理)

代理

使用范围

1个对象只能设置一个代理(假设这个对象只有1个代理属性)

1个对象能成为多个对象的代理

比通知规范

建议使用代理多于通知

六 购物车细节完善

/** 购物车对象(存放需要购买的商品) */
@property (nonatomic, strong) NSMutableArray *wineCar;


实现代理方法

- (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 > 0;

// 将商品从购物车中移除
if (wineCell.wine.count == 0) {
[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];
}


按钮点击

减少遍历次数

- (IBAction)clear {
// 将模型里面的count清零
for (XMGWine *wine in self.wineCar) {
wine.count = 0;
}
// 刷新
[self.tableView reloadData];

// 清空购物车数据
[self.wineCar removeAllObjects];

self.buyButton.enabled = NO;
self.totalPriceLabel.text = @"0";
}

- (IBAction)buy {
// 打印出所有要买的东西
for (XMGWine *wine in self.wineCar) {
NSLog(@"%d件【%@】", wine.count, wine.name);
}
}


七 键盘处理

退出键盘方法1:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 文本框不再是第一响应者,就会退出键盘
[self.textField resignFirstResponder];
}


退出键盘方法2:

结束编辑,缺点有很多文本框时退出键盘要拿到所有textField比较麻烦

[self.textField endEditing:YES];


退出键盘方法3:

[self.view endEditing:YES];


创建键盘通知

- (void)viewDidLoad {
[super viewDidLoad];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
}

- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }


监听键盘的frame即将发生改变的时候调用

- (void)keyboardWillChange:(NSNotification *)note
{
// 获得键盘的frame
CGRect frame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];

// 修改底部约束
self.bottomSpace.constant = self.view.frame.size.height - frame.origin.y;

//  执行动画
CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:duration animations:^{
[self.view layoutIfNeeded];
}];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: