我有一个模型对象,它包含一个布尔标志。我想使用UISwitch来显示标志的值。可以通过两种方式更改该值:
但是,在timer方法中设置开关的值具有这样的效果:即使用户已经触摸了开关,有时也不会触发touchUpInside操作。
因此,我面临以下问题:当状态外部更改时,如果在计时器中设置开关状态,则会从用户那里释放一些状态更改。如果我不使用计时器,就会从用户那里得到所有的状态更改。然而,我错过了所有外部状态的改变。
现在我的想法已经用完了。如何实现我想要的,在模型中获得两种类型的状态更改,并将它们正确地反映在开关视图中?
下面是一个很小的例子,说明了这个问题。我用一个简单的布尔标志替换了模型对象,在计时器中我根本不改变标志,我只调用setOn:动画:。我数一数动作方法的调用。就像这样,我可以很容易地发现有多少触觉被遗漏了:
#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发布于 2014-06-28 15:11:53
我的原始代码与iOS 7中预期的一样工作,因此它似乎是iOS 6中的一个bug。
发布于 2012-08-22 19:11:45
使用UIControlEventValueChanged确定开关何时已切换(以编程方式或由用户切换)。不要使用touchUpInside。而且,当用户拖动touchUpInside时,UISwitch也不能很好地工作。
另外,不要复制已经由UISwitch (即"on“属性)维护的属性(例如,您的值属性),该属性是冗余的,只会给您带来麻烦。
发布于 2012-08-22 19:11:08
编辑:下面的代码做你想做的事情,如果不是你想要的方式的话。如果确保对控件的计时器更改和触摸更改都会接收操作方法。每个更改只发送一次该方法,这是您希望的结果。
注意切换到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:我刚在你的答案中看到了新代码,并试用了一下。我不确定它是否做了我想做的事。然而,我不确定我是否正确地理解了它。在这两种情况下我都不想做同样的事。
嗯,那不太可能。但是,您当然可以在action方法中添加一个计数器,并查看您所做的敲击是否与计数器相等。当用户点击开关时,然后是“发件人== theSwitch”。如果以其他方式发送操作方法,则可以使用不同的发送方(以区分它们)。
没关系--但是你一直说你想要调用动作方法。这个代码就是这么做的。如果您错误地说明了这一点,并且不希望调用action方法,那么删除" 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];
}
}
}
}当然,我也想实现同样的目标:模型状态在视图中得到了正确的反映。这有意义吗?
坦率地说,你并没有很好地描述你想要的东西,我一直在回应你的评论,但即使现在我仍然不完全清楚。我相信你有你想做的任何事所需要的所有信息。
https://stackoverflow.com/questions/12079770
复制相似问题