保持UISegmentedControl(以及其他)响应

时间:2009-07-29 05:00:00

标签: iphone objective-c cocoa-touch multithreading

我有一个分段控件用作切换。当切换时,我在表格视图中切换一堆内容,这需要一个微小但明显的时间(在表格视图中插入/删除部分,动画更改等)。我希望分段控件立即响应选择。因此,在我的动作处理分段控件的UIControlEventValueChanged事件的代码中,我执行以下操作:

- (IBAction)groupingChanged:(id)sender {
    UISegmentedControl *seg = sender;
    [tableModel toggleOn:[seg selectedSegmentIndex] == ToggleOnIndex];
    [self performSelectorOnMainThread:@selector(updateGrouping)
                           withObject:nil
                        waitUntilDone:NO];
}

updateGrouping的位置:

- (void)updateGrouping {
    MXAssertMainThread();

    [tableView beginUpdates];
    ... several table updates
    [tableView endUpdates];
}

设置waitUntilDone:NO允许groupingChanged方法在调用updateGrouping之前完成,但这似乎不足以重新绘制视图。分段控制直到表更新完成,然后切换。

所以我尝试修改groupingChanged:来为更新创建一个线程,如下所示:

- (void)delayed {
    [self performSelectorOnMainThread:@selector(updateGrouping)
                           withObject:nil
                        waitUntilDone:NO];
}

- (IBAction)groupingChanged:(id)sender {
    UISegmentedControl *seg = sender;
    [tableModel toggleOn:[seg selectedSegmentIndex] == ToggleOnIndex];
    [self performSelectorInBackground:@selector(delayed) withObject:nil];
}

这确实有效。分段控件立即切换,表格很快就会出现。但我对结果并不十分自信。是否只是在新线程启动时给予主线程缓冲的副作用?这是我需要将更新排队到UI的方式吗?这显然是hacky。我希望有人有一个更好的模式,他们正在关注这种情况。

3 个答案:

答案 0 :(得分:2)

以这种方式思考 - 您所做的一切通常都是在主线程上完成的,包括UI更新。

因此,在原始代码中,更新表视图的代码是在对段执行UI更新的代码之前到达的。

所以,你是正确的,给主线程一个中断允许更多的时间来完成UI的更新,因为主线程被允许完成UI更新,而后台线程处理表更新。

您可以尝试的另一种方法是使用performSelector:withObject:afterDelay,延迟为0.0(允许主线程在继续使用选择器之前处理其他事情)。它可能在performSelectorOnMainThread没有的地方起作用,因为即使它们最终做了非常相似的事情,该调用也可能更直接。

答案 1 :(得分:1)

如果您只是想确保分段控件快速重新绘制,我可能不会深入研究线程。

相反,我只是设置一个像0.1这样的低值的计时器,这应该足以让控件更新而不会对用户造成任何明显的延迟。

当我有很多工作要做但需要快速更新UI时,我已经习惯了这个。

仍然有点“hacky”,但没有引入线程。

因此...

- (IBAction)groupingChanged:(id)sender {
    UISegmentedControl *seg = sender;
    [tableModel toggleOn:[seg selectedSegmentIndex] == ToggleOnIndex];
    [NSTimer scheduledTimerWithTimeInterval:0.1 
                                     target:self 
                                   selector:@selector(updateGrouping) 
                                   userInfo:nil 
                                    repeats:NO];    

}

答案 2 :(得分:0)

我遇到了同样的问题并通过继承UISegmentedControl来创建一个Delayed UISegmentedControl来解决它。

在延迟控制中,我覆盖addTarget:action:forControlEvents:以捕获目标&行动。然后,当分段事件发生时,我运行NSTimer以启动捕获的目标&设定延迟后的行动。结果是UI被更新以显示单击的段,我可以像使用UISegmentedControl一样使用DelayedUISegmentedControl:

// Follows all normal initialization patterns of UISegmentedControl
UISegmentedControl *segmentedControl = [[DelayedUISegmentedControl alloc] 
    initWithItems:[NSArray arrayWithObjects: @"First", @"Second", nil]];

// Adds a delay to the selector; default is 0.25
[segmentedControl addTarget:self action:@selector(segmentAction:) 
    forControlEvents:UIControlEventValueChanged];

如果您要下载控件,我已在google code上开源。