IOS开发UI进阶之UITableView四

来源:互联网 发布:乌克兰男女比例知乎 编辑:程序博客网 时间:2024/05/28 15:52

一 购物车

骨架搭建

- (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];    }];}
0 0
原创粉丝点击