通知UITableView有关dataSource的更改

时间:2015-04-06 05:26:40

标签: ios objective-c iphone uitableview

UITableView在单元格中随时间动态更改了数据,即我为每个单元格“AVMFilmsStore”存储数据的对象提供了自定义类。这个类有自己更新数据的方法(它每隔10秒从服务器获得进展),如:

   -(void)getProgress{
 // some operations
 ...

 if (progress<100){
 [self getProgressWithDelay:10];
 }
}

getProgressWithDelay方法是:

-(void)getProgressWithDelay:(float)delay{
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,(int64_t)(delay * NSEC_PER_SEC));

dispatch_after(time,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    [self getProgress];
    });
}

每次调用此方法后,我都会检查progress值是否已更改,我需要告诉我的主类UITableView它必须重新绘制进度发生变化的每个单元格。我有自定义UITableViewCell类,其中包含方法:

-(void)styleForReady:(BOOL)isReady andProgress:(float)progress{
if(!isReady){
    self.movieDate.hidden = YES;
    self.movieName.hidden = YES;
    self.playButton.hidden = YES;
    self.settingsButton.hidden = YES;
    self.progressLabel.hidden = NO;
    self.progressLine.hidden = NO;
    self.backgroundImage.hidden = YES;
    self.progressLine.transform = CGAffineTransformMakeScale(1.0, 4.0);
    self.progressLine.progressTintColor = MINT_1_0;
    self.progressLabel.textColor = [UIColor blackColor];
    self.bgView.hidden = YES;
    self.progressLabel.text = NSLocalizedString(@"movieCreating", nil);
    [self.progressLine setProgress:progress animated:YES];

} else{
    self.movieDate.hidden = NO;
    self.movieName.hidden = NO;
    self.playButton.hidden = NO;
    self.settingsButton.hidden = NO;
    self.progressLabel.hidden = YES;
    self.progressLine.hidden = YES;
    self.backgroundImage.hidden = NO;
    self.bgView.hidden = NO;
}

我在cellForRowAtIndexPath中将此方法称为:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"readyCell";
AVMMovieCell  *cell = [self.readyTable dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Configure the cell...
if (cell == nil) {
    cell = (AVMMovieCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
}
AVMFilmsStore *oneItem = [readyArray objectAtIndex:indexPath.row];


if (oneItem.ready==1){
 [cell styleForReady:YES andProgress:100];
} else { //если идет сборка
    [cell styleForReady:NO andProgress:[oneItem getProgress]];

}

    return cell;

}

通过cellForRowAtIndexPath通知我的班级我在此课程的viewDidLoad方法中注册了NSNotification,然后我在getProgress AVMFilmsStore发送了通知。

当我的主课程收到通知时,它会在每个所需的单元格更新中调用[tableView reloadData];方法和progressView。 问题是progressView的更新仅在我滚动UITableView虽然dataSource已更改时才会发生。 它继续我上一个问题Check progress where is necessary UITableView 我对这个问题很困惑。任何建议都会有所帮助。对不起这么愚蠢的问题,但我不知道如何解决它。

修改    这就是我在主类中注册NSNotifications的方式:

-(void)viewDidLoad{
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(fsProgressChanged:)
                                             name:@"filmStoreProgressChanged"
                                           object:nil];
}

- (void)fsProgressChanged:(NSNotification *)notification
{

if ([[notification name] isEqualToString:@"filmStoreProgressChanged"]) {
    [readyTable reloadData];
    }
}

以及我如何在AVMFilmsStore中发送通知: 我在这个课程中有@property readyProgress,每次我在这个方法上取得进步,我都会将进度存储到readyProgress然后我这样做:

-(void)getProgress{
 // some operations
 ...

 if (progress<100){
     if(self.readyProgress!=progress){
      [[NSNotificationCenter defaultCenter] postNotificationName:@"filmStoreProgressChanged" object:nil];
   }
 [self getProgressWithDelay:10];
 }
}

2 个答案:

答案 0 :(得分:1)

您可以在此处使用键值观察(KVO)方法。首先,您应该在单元格中保留相关AVMFilmsStore类的属性/实例。

@property(nonatomic, strong) AVMFilmsStore *filmStore;

然后在filmStore单元格的setter方法中添加属性的观察者,

- (void)setFilmStore:(AVMFilmsStore *)filmStore {

  _filmStore = filmStore;

  [_filmStore addObserver:self forKeyPath:@"ready" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}

然后实现方法,

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

    if([keyPath isEqualToString:@"ready"])
    {
      id oldC = [change objectForKey:NSKeyValueChangeOldKey];
      id newC = [change objectForKey:NSKeyValueChangeNewKey];

      // Handle changes or update cell here
    }
}

删除dealloc中的观察者,

- (void)dealloc {

  [_filmStore removeObserver:self forKeyPath:@"ready"];
}

在你的viewController中,

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  // Cell creation

  cell.filmStore = [readyArray objectAtIndex:indexPath.row];

  return cell;
}

答案 1 :(得分:0)

我认为您的问题是您在后台线程上使用dispatch_after启动整个更新过程。你必须确保当你调用实际上会更新表的代码时,你就是主线程。

尝试做这样的事情:

- (void)fsProgressChanged:(NSNotification *)notification
{

    if ([[notification name] isEqualToString:@"filmStoreProgressChanged"]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [readyTable reloadData];
        });
    }
}

修改

我做了这个测试类,我可以模拟你的错误并且修复工作正常:

class ViewController: UITableViewController {

    private struct Constants {
        static let Notification = "notification"
        static let CellIdentifier = "cell"
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "notificationFired:", name: Constants.Notification, object: nil)
        tableView.reloadData()
    }

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(Constants.CellIdentifier, forIndexPath: indexPath) as UITableViewCell
        cell.backgroundColor = UIColor.random
        return cell
    }

    func notificationFired(notification: NSNotification) {
        dispatch_async(dispatch_get_main_queue()) { () -> Void in
            self.tableView.reloadData()
        }
    }

    @IBAction func fireNotification(sender: UIBarButtonItem) {
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))

        dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) { () -> Void in
            NSNotificationCenter.defaultCenter().postNotificationName(Constants.Notification, object: self)
        }
    }
}

private extension UIColor {
    class var random : UIColor {
        switch arc4random() % 5 {
        case 0: return UIColor.greenColor()
        case 1: return UIColor.blueColor()
        case 2: return UIColor.orangeColor()
        case 3: return UIColor.redColor()
        case 4: return UIColor.purpleColor()
        default: return UIColor.blackColor()
        }
    }
}