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]; }]; }
相关文章推荐
- TrueType和Bitmap字体的区别
- TrueType和Bitmap字体的区别
- TrueType和Bitmap字体的区别
- UIView与CALayer
- UI 动画之UIView动画的 四种 使用含有block的类方法 实现动画
- UI 动画之反射变换的旋转
- TrueType字体的后缀名解释
- TrueType字体的后缀名解释
- TrueType字体的后缀名解释
- UI 动画之反射变换的缩放
- 使用UGUI实现拖拽功能(拼图小游戏)
- iOS UITableview 只刷新一个cell 或 section
- UI14_图片相关
- UI 动画之UIView动画之间的一些方法
- UISearchBar
- WinForm界面开发之布局控件"WeifenLuo.WinFormsUI.Docking"的使用
- UI 动画之反射变换的平移
- UIsearchBar
- SpriteBuilder中本地化的局限性
- SpriteBuilder中本地化的局限性