1. 程式人生 > >【iOS】KVO方式監聽陣列的變化動態重新整理tableView

【iOS】KVO方式監聽陣列的變化動態重新整理tableView

寫作本文來由:   iOS預設不支援對陣列的KVO,因為普通方式監聽的物件的地址的變化,而陣列地址不變,而是裡面的值發生了改變

整個過程需要三個步驟 (與普通監聽一致)

/*

    *  第一步建立觀察者及觀察的物件

    *  第二步 處理key的變化(根據key的變化重新整理UI)

    *  第三步 移除觀察者

*/

陣列不能放在UIViewController裡面,在這裡面的陣列是監聽不到陣列大小的變化的,需要將需要監聽的陣列封裝到model裡面<

model類為: 將監聽的陣列封裝到model裡,不能監聽UIViewController裡面的陣列

兩個屬性 一個 字串類的姓名,一個數組類的modelArray,我們需要的就是監聽modelArray裡面元素的變化

@interface model : NSObject
@property(nonatomic, copy)NSString *name;
@property(nonatomic, retain)NSMutableArray *modelArray;
1 建立觀察者及觀察的物件

   第一步  建立觀察者及觀察的物件

    [_modeladdObserver:selfforKeyPath:@"modelArray"options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOldcontext:NULL];

   第二步

 處理key的變化(根據key的變化重新整理UI)

    最重要的就是新增資料這裡

    不能這樣 [_model.modelArray addObject]方法,需要這樣呼叫   [[_model mutableArrayValueForKey:@"modelArray"] addObject:str];原因稍後說明。

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

{
    if ([keyPath isEqualToString:@"modelArray"]) {
        [_tableView reloadData];
    }
}
    第三步 移除觀察者
    if (_model != nil) {
        [_model removeObserver:self forKeyPath:@"modelArray"];
    }

以下附上本文程式碼:

程式碼中涉及三點

1 根據陣列動態重新整理tableview;

2 定時器的使用(涉及迴圈引用問題);

3 使用KVC優化model的初始化程式碼。

沒找到上傳整個工程的方法,暫時附上程式碼

1  NSTimer相關

//
//  NSTimer+DelegateSelf.h
//  KVOTableview
//
//  Created by 程立彬 on 14-8-8.
//  Copyright (c) 2014年 chenglb. All rights reserved.
//

//為防止controller和nstimer之間的迴圈引用,delegate指向當前單例,而不指向controller

#import <Foundation/Foundation.h>

@interface NSTimer (DelegateSelf)

+(NSTimer *)scheduledTimerWithTimeInterval:(int)timeInterval block:(void(^)())block repeats:(BOOL)yesOrNo;

@end

//
//  NSTimer+DelegateSelf.m
//  KVOTableview
//
//  Created by 程立彬 on 14-8-8.
//  Copyright (c) 2014年 chenglb. All rights reserved.
//

#import "NSTimer+DelegateSelf.h"

@implementation NSTimer (DelegateSelf)

+(NSTimer *)scheduledTimerWithTimeInterval:(int)timeInterval block:(void(^)())block repeats:(BOOL)yesOrNo
{
    return [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(callBlock:) userInfo:[block copy] repeats:yesOrNo];
}


+(void)callBlock:(NSTimer *)timer
{
    void(^block)() = timer.userInfo;
    if (block != nil) {
        block();
    }
}

@end

2 model相關
//
//  model.h
//  KVOTableview
//
//  Created by 程立彬 on 14-8-8.
//  Copyright (c) 2014年 chenglb. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface model : NSObject
@property(nonatomic, copy)NSString *name;
@property(nonatomic, retain)NSMutableArray *modelArray;

-(id)initWithDic:(NSDictionary *)dic;

@end

//
//  model.m
//  KVOTableview
//
//  Created by 程立彬 on 14-8-8.
//  Copyright (c) 2014年 chenglb. All rights reserved.
//


//KVC的應用  簡化冗餘程式碼
#import "model.h"

@implementation model

-(id)initWithDic:(NSDictionary *)dic
{
    self = [super init];
    if (self) {
        [self setValuesForKeysWithDictionary:dic];
    }
    
    return self;
}

-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
    NSLog(@"undefine key ---%@",key);
}

@end

3 UIViewController相關
//
//  RootViewController.m
//  KVOTableview
//
//  Created by 程立彬 on 14-8-8.
//  Copyright (c) 2014年 chenglb. All rights reserved.
//

/*
    *  第一步 建立觀察者及觀察的物件
    *  第二步 處理key的變化(根據key的變化重新整理UI)
    *  第三步 移除觀察者
 
*/

#import "RootViewController.h"
#import "NSTimer+DelegateSelf.h"
#import "model.h"

#define TimeInterval 3.0

@interface RootViewController ()<UITableViewDelegate,UITableViewDataSource>

@property(nonatomic, retain)NSTimer *timer;
@property(nonatomic, retain)UITableView    *tableView;
@property(nonatomic, retain)model *model;

@end

@implementation RootViewController

- (void)dealloc
{
    //第三步
    if (_model != nil) {
        [_model removeObserver:self forKeyPath:@"modelArray"];
    }
    //停止定時器
    if (_timer != nil) {
        [_timer invalidate];
        _timer = nil;
    }
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {

        NSDictionary *dic = [NSDictionary dictionaryWithObject:[NSMutableArray arrayWithCapacity:0] forKey:@"modelArray"];
        
        self.model = [[model alloc] initWithDic:dic];
    
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //第一步
    [_model addObserver:self forKeyPath:@"modelArray" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];
    
    self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
    _tableView.delegate        = self;
    _tableView.dataSource      = self;
    _tableView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:_tableView];
    
    //定時新增資料
    [self startTimer];
    
}

//新增定時器
-(void)startTimer
{
    __block RootViewController *bself = self;
    
    _timer = [NSTimer scheduledTimerWithTimeInterval:TimeInterval block:^{
        
        [bself changeArray];
    } repeats:YES];

}

//增加陣列中的元素 自動重新整理tableview
-(void)changeArray
{
    
    NSString *str = [NSString stringWithFormat:@"%d",arc4random()%100];
    [[_model mutableArrayValueForKey:@"modelArray"] addObject:str];
    
}


//第二步 處理變化
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"modelArray"]) {
        [_tableView reloadData];
    }
}




-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return  [_model.modelArray count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    static NSString *cellidentifier = @"cellIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellidentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellidentifier];
    }
    
    cell.textLabel.text = _model.modelArray[indexPath.row];
    return cell;
}


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end