我有一个包含布尔标志的模型对象。我想使用UISwitch显示标志的值。可以通过两种方式更改值:
但是,在timer方法中设置开关的值会导致即使用户触摸了开关,有时也不会触发touchUpInside操作。
所以我面临以下问题:如果我在外部状态发生变化时在定时器中设置了开关状态,我会从用户那里放松一些状态变化。如果我不使用计时器,我会收到用户的所有状态更改。但是,我错过了所有外部状态变化。
现在我的想法已经用完了。如何实现我想要的,在模型中获得两种类型的状态更改并在交换机视图中正确反映它们?
这是一个显示问题的最小示例。我已经用一个简单的布尔标志替换了模型对象,并且在计时器中我根本没有改变标志,我只是调用setOn:animated:。我统计了action方法的调用。像这样我可以很容易地找出错过了多少次触摸:
#import "BPAppDelegate.h"
#import "BPViewController.h"
@implementation BPAppDelegate {
NSTimer *repeatingTimer;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
BPViewController *viewController = [[BPViewController alloc] init];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
[self startTimer];
return YES;
}
- (void) startTimer {
repeatingTimer = [NSTimer scheduledTimerWithTimeInterval: 0.2
target: self.window.rootViewController
selector: @selector(timerFired:)
userInfo: nil
repeats: YES];
}
@end
#import "BPViewController.h"
@implementation BPViewController {
UISwitch *uiSwitch;
BOOL value;
int count;
}
- (void)viewDidLoad
{
[super viewDidLoad];
value = true;
uiSwitch = [[UISwitch alloc] init];
uiSwitch.on = value;
[uiSwitch addTarget:self action:@selector(touchUpInside:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:uiSwitch];
}
- (void)touchUpInside: (UISwitch *)sender {
count++;
value = !value;
NSLog(@"touchUpInside: value: %d, switch: %d, count: %d", value, sender.isOn, count);
}
- (void) timerFired: (NSTimer*) theTimer {
NSLog(@"timerFired: value: %d, switch: %d, count: %d", value, uiSwitch.isOn, count);
// set the value according to some external state. For the example just leave it.
[uiSwitch setOn:value animated:false];
}
@end
答案 0 :(得分:4)
使用UIControlEventValueChanged确定切换的时间(以编程方式或由用户切换)。不要使用touchUpInside。
另外,不要复制已经由UISwitch维护的属性(例如你的value属性)(即“on”属性)......它是多余的,只会让你遇到麻烦
答案 1 :(得分:2)
编辑:以下代码完全符合您的要求,如果不是您想要的话。如果确保定时器更改和触摸控件的更改都会收到操作方法。该方法仅在每次更改时发送一次,这有望成为您想要的。
注意切换到UIControlEventValueChanged。
[sw addTarget:self action:@selector(touched:) forControlEvents:UIControlEventValueChanged];
- (void) timerFired: (NSTimer*) theTimer
{
// set the value according to some external state. For the example just leave it.
BOOL oldOn = sw.on;
BOOL newOn = YES;
[sw setOn:newOn];
if(newOn != oldOn) {
for(id target in [sw allTargets]) {
for(NSString *action in [sw actionsForTarget:target forControlEvent:UIControlEventValueChanged]) {
[target performSelector:NSSelectorFromString(action) withObject:self];
}
}
}
}
EDIT2: 我[原创海报]刚看到你的答案中的新代码并试了一下。我不确定它是否正是我想要的。但是,我不确定我是否理解正确。在这两种情况下我都不想做同样的事情。
多数民众赞成 - 但你一直说你想要调用动作方法。这段代码就是这样。如果您错误地说明了这一点,并且您不想调用操作方法,则删除“if”块。
- (void)insureValueIs:(BOOL)val
{
BOOL oldVal = sw.on;
if(val != oldVal) {
for(id target in [sw allTargets]) {
for(NSString *action in [sw actionsForTarget:target forControlEvent:UIControlEventValueChanged]) {
[target performSelector:NSSelectorFromString(action) withObject:self];
}
}
}
}
当然,我确实想要实现同样的事情:模型状态在视图中正确反映。那有意义吗?
坦率地说,你没有很好地描述你想要的东西,我一直在回应你的评论,但即使是现在我仍然不完全清楚。我相信你拥有所需的所有信息,可以随心所欲地做任何事情。答案 2 :(得分:1)
您的计时器经常触发它可能会阻止切换完成动画转换到备用值,因此它永远不会发送valueChanged事件消息。您每隔0.2秒将值设置为YES,我认为这比用户点击开关时动画的持续时间更快。
答案 3 :(得分:0)
我的原始代码在iOS 7中按预期工作。因此它似乎是iOS 6中的一个错误。