UISwitch 值更改事件在 UITextField 编辑结束之前触发
UISwitch value changed event fires before UITextField editing did end
在我的应用程序中,UITableViewCell
列表中显示了许多 UISwitch
和 UITextField
。
当用户开始编辑 UITextField
然后点击 UISwitch
时,事件的顺序导致 UITextField
显示 UISwitch
的值,因为事件处理程序未收到 UITextField
的结束编辑事件。
如何可靠地确保 UITextField
的 UIControlEventEditingDidEnd
事件在 UISwitch
的 UIControlEventValueChanged
之前触发?
它会导致这样的错误(文本字段中显示的开关值):
步骤(应该发生什么):
1.Tap UISwitch
激活它
UISwitch:startEditing:switch243
UISwitch:valueChanged:{true}
UISwitch:endEditing
UIEventHandler:saveValue:switch243:{true}
2.Tap UITextField
开始编辑吧
UITextField:startEditing:textfield455
3.Tap UISwitch
停用它
UITextField:endEditing
UISwitch:startEditing:switch243
UISwitch:valueChanged:{false}
UISwitch:endEditing
UIEventHandler:saveValue:switch243:{false}
控制台日志(真正发生了什么 - UISwitch 事件在 UITextField:endEditing 之前触发):
UISwitch:startEditing:switch243
UISwitch:valueChanged:{true}
UISwitch:endEditing
UIEventHandler:saveValue:switch243:{true}
UITextField:startEditing:textfield455
UISwitch:startEditing:switch243
UISwitch:valueChanged:{false}
UISwitch:endEditing
UIEventHandler:saveValue:switch243:{false}
UITextField:endEditing
实施:
UITableViewCellWithSwitch.h
:
@interface UITableViewCellWithSwitch : UITableViewCell
@property (nonatomic, strong) NSString *attributeID;
@property (nonatomic, retain) IBOutlet UISwitch *switchField;
@end
UITableViewCellWithSwitch.m
:
@implementation UITableViewCellWithSwitch
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.switchField addTarget:self
action:@selector(switchChanged:)
forControlEvents:UIControlEventValueChanged];
}
return self;
}
// UIControlEventValueChanged
- (void)switchChanged:(UISwitch *)sender {
NSLog(@"UISwitch:startEditing:%@",self.attributeID);
[self handleStartEditingForAttributeID:self.attributeID];
NSString* newValue = sender.on==YES?@"true":@"false";
NSLog(@"UISwitch:valueChanged:{%@}", newValue);
[self handleValueChangeForEditedAttribute:newValue];
NSLog(@"UISwitch:endEditing");
[self handleEndEditingForEditedAttribute];
}
@end
UITableViewCellWithTextField.h
:
@interface UITableViewCellWithTextField : UITableViewCell<UITextFieldDelegate>
@property (nonatomic, strong) NSString *attributeID;
@property (strong, nonatomic) IBOutlet UITextField *inputField;
@end
UITableViewCellWithTextField.m
:
@implementation UITableViewCellWithTextField
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.inputField addTarget:self
action:@selector(textFieldDidBegin:)
forControlEvents:UIControlEventEditingDidBegin];
[self.inputField addTarget:self
action:@selector(textFieldDidChange:)
forControlEvents:UIControlEventEditingChanged];
[self.inputField addTarget:self
action:@selector(textFieldDidEnd:)
forControlEvents:UIControlEventEditingDidEnd];
}
return self;
}
// UIControlEventEditingDidBegin
-(void) textFieldDidBegin:(UITextField *)sender {
NSLog(@"UITextField:startEditing:%@",self.attributeID);
[self handleStartEditingForAttributeID:self.attributeID];
}
// UIControlEventEditingChanged
-(void) textFieldDidChange:(UITextField *)sender {
NSLog(@"UITextField:valueChanged:{%@}", sender.text);
[self handleValueChangeForEditedAttribute:sender.text];
}
// UIControlEventEditingDidEnd
-(void) textFieldDidEnd:(UITextField *)sender {
NSLog(@"UITextField:endEditing");
[self handleEndEditingForEditedAttribute];
}
@end
UIEventHandler.m
聚合所有 UI 编辑事件:
-(void) handleStartEditingForAttributeID:(NSString *)attributeID {
// Possible solution
//if (self.editedAttributeID != nil && [attributeID isEqualToString:self.editedAttributeID]==NO) { // Workaround needed for UISwitch events
// [self handleEndEditingForActiveAttribute];
//}
self.editedAttributeID = attributeID;
self.temporaryValue = nil;
}
-(void) handleValueChangeForEditedAttribute:(NSString *)newValue {
self.temporaryValue = newValue;
}
-(void) handleEndEditingForEditedAttribute {
if (self.temporaryValue != nil) { // Only if value has changed
NSLog(@"UIEventHandler:saveValue:%@:{%@}", self.editedAttributeID, self.temporaryValue);
// Causes the view to regenerate
// The UITextField loses first responder status and UIControlEventEditingDidEnd is gets triggered too late
[self.storage saveValue:self.temporaryValue
forAttribute:self.editedAttributeID];
self.temporaryValue = nil;
}
self.editedAttributeID = nil;
}
如果我理解正确,您遇到的问题是当文本字段是第一响应者时更改开关值,然后您的文本字段的文本将更新为开关的值。
A UITextField
的 didEndEditing:
事件仅在文本字段退出第一响应者时才会发生。如果您只想确保当开关值更改时文本字段结束编辑,您应该在开关收到 UIControlEventValueChanged
事件时将 endEditing:
消息发送到活动文本字段。
现在如何调用文本字段中的 endEditing:
消息取决于您的 classes 的结构。您可以在 table 视图单元格 class 上指定一个初始化方法,您可以在其中传递与控制文本字段的 UISwitch
相对应的 UITextField
实例。保留对文本字段实例的弱引用,然后在开关值更改时调用 endEditing:
。或者您可以简单地尝试在触发 switchChanged:
事件或 [self.superview endEditing:YES];
时在 UITableViewCellWithSwitch
上调用 [self endEditing:YES];
。我更喜欢前一种解决方案而不是后者,因为后者更像是一种黑客而不是正确的解决方案。
更新:
检查您的代码后,您在问题中提到的错误的原因
It leads to errors like this (value of switch displayed in text field):
是下面的一段代码:
- (void)switchChanged:(UISwitch *)sender {
NSLog(@"UISwitch:startEditing:%@",self.attributeID);
[self handleStartEditingForAttributeID:self.attributeID];
NSString* newValue = sender.on==YES?@"true":@"false";
NSLog(@"UISwitch:valueChanged:{%@}", newValue);
[self handleValueChangeForEditedAttribute:newValue];
NSLog(@"UISwitch:endEditing");
[self handleEndEditingForEditedAttribute];
}
您调用 handleValueChangeForEditedAttribute:
时使用的属性开关值实际上只应保存文本字段的值。在您的 UIEventHandler
class 中,您正在使用 handleEndEditingForEditedAttribute
方法中的开关值更新您的数据访问对象 (DAO)。将您的 switchChanged:
方法的逻辑更改为如下内容:
- (void)switchChanged:(UISwitch *)sender {
if (sender.on == YES) {
[self handleStartEditingForAttributeID:self.attributeID];
} else {
[self handleEndEditingForEditedAttribute];
}
}
并且在您的 UIEventHandler
class 中,取消注释 post 中的 "possible solution" 注释行。这应该确保在存储新 attributeID
.
的值之前保存任何先前的更改
-(void) handleStartEditingForAttributeID:(NSString *)attributeID {
// Possible solution
if (self.editedAttributeID != nil && [attributeID isEqualToString:self.editedAttributeID]==NO) { // Workaround needed for UISwitch events
[self handleEndEditingForActiveAttribute];
}
self.editedAttributeID = attributeID;
self.temporaryValue = nil;
}
我最好的解决方案是解决 UIEventHandler.m
中的问题。如果在调用 startEditing
时 endEditing
事件尚未触发,它会从 UIEventHandler
.
中调用
-(void) handleStartEditingForAttributeID:(NSString *)attributeID {
// Possible solution
if (self.editedAttributeID != nil && [attributeID isEqualToString:self.editedAttributeID]==NO) { // Workaround needed for UISwitch events
[self handleEndEditingForActiveAttribute];
}
self.editedAttributeID = attributeID;
self.temporaryValue = nil;
}
-(void) handleValueChangeForEditedAttribute:(NSString *)newValue {
self.temporaryValue = newValue;
}
-(void) handleEndEditingForEditedAttribute {
if (self.temporaryValue != nil) { // Only if value has changed
NSLog(@"UIEventHandler:saveValue:%@:{%@}", self.editedAttributeID, self.temporaryValue);
// Causes the view to regenerate
// The UITextField loses first responder status and UIControlEventEditingDidEnd is gets triggered too late
[self.storage saveValue:self.temporaryValue
forAttribute:self.editedAttributeID];
self.temporaryValue = nil;
}
self.editedAttributeID = nil;
}
在我的应用程序中,UITableViewCell
列表中显示了许多 UISwitch
和 UITextField
。
当用户开始编辑 UITextField
然后点击 UISwitch
时,事件的顺序导致 UITextField
显示 UISwitch
的值,因为事件处理程序未收到 UITextField
的结束编辑事件。
如何可靠地确保 UITextField
的 UIControlEventEditingDidEnd
事件在 UISwitch
的 UIControlEventValueChanged
之前触发?
它会导致这样的错误(文本字段中显示的开关值):
步骤(应该发生什么):
1.Tap UISwitch
激活它
UISwitch:startEditing:switch243
UISwitch:valueChanged:{true}
UISwitch:endEditing
UIEventHandler:saveValue:switch243:{true}
2.Tap UITextField
开始编辑吧
UITextField:startEditing:textfield455
3.Tap UISwitch
停用它
UITextField:endEditing
UISwitch:startEditing:switch243
UISwitch:valueChanged:{false}
UISwitch:endEditing
UIEventHandler:saveValue:switch243:{false}
控制台日志(真正发生了什么 - UISwitch 事件在 UITextField:endEditing 之前触发):
UISwitch:startEditing:switch243
UISwitch:valueChanged:{true}
UISwitch:endEditing
UIEventHandler:saveValue:switch243:{true}
UITextField:startEditing:textfield455
UISwitch:startEditing:switch243
UISwitch:valueChanged:{false}
UISwitch:endEditing
UIEventHandler:saveValue:switch243:{false}
UITextField:endEditing
实施:
UITableViewCellWithSwitch.h
:
@interface UITableViewCellWithSwitch : UITableViewCell
@property (nonatomic, strong) NSString *attributeID;
@property (nonatomic, retain) IBOutlet UISwitch *switchField;
@end
UITableViewCellWithSwitch.m
:
@implementation UITableViewCellWithSwitch
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.switchField addTarget:self
action:@selector(switchChanged:)
forControlEvents:UIControlEventValueChanged];
}
return self;
}
// UIControlEventValueChanged
- (void)switchChanged:(UISwitch *)sender {
NSLog(@"UISwitch:startEditing:%@",self.attributeID);
[self handleStartEditingForAttributeID:self.attributeID];
NSString* newValue = sender.on==YES?@"true":@"false";
NSLog(@"UISwitch:valueChanged:{%@}", newValue);
[self handleValueChangeForEditedAttribute:newValue];
NSLog(@"UISwitch:endEditing");
[self handleEndEditingForEditedAttribute];
}
@end
UITableViewCellWithTextField.h
:
@interface UITableViewCellWithTextField : UITableViewCell<UITextFieldDelegate>
@property (nonatomic, strong) NSString *attributeID;
@property (strong, nonatomic) IBOutlet UITextField *inputField;
@end
UITableViewCellWithTextField.m
:
@implementation UITableViewCellWithTextField
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.inputField addTarget:self
action:@selector(textFieldDidBegin:)
forControlEvents:UIControlEventEditingDidBegin];
[self.inputField addTarget:self
action:@selector(textFieldDidChange:)
forControlEvents:UIControlEventEditingChanged];
[self.inputField addTarget:self
action:@selector(textFieldDidEnd:)
forControlEvents:UIControlEventEditingDidEnd];
}
return self;
}
// UIControlEventEditingDidBegin
-(void) textFieldDidBegin:(UITextField *)sender {
NSLog(@"UITextField:startEditing:%@",self.attributeID);
[self handleStartEditingForAttributeID:self.attributeID];
}
// UIControlEventEditingChanged
-(void) textFieldDidChange:(UITextField *)sender {
NSLog(@"UITextField:valueChanged:{%@}", sender.text);
[self handleValueChangeForEditedAttribute:sender.text];
}
// UIControlEventEditingDidEnd
-(void) textFieldDidEnd:(UITextField *)sender {
NSLog(@"UITextField:endEditing");
[self handleEndEditingForEditedAttribute];
}
@end
UIEventHandler.m
聚合所有 UI 编辑事件:
-(void) handleStartEditingForAttributeID:(NSString *)attributeID {
// Possible solution
//if (self.editedAttributeID != nil && [attributeID isEqualToString:self.editedAttributeID]==NO) { // Workaround needed for UISwitch events
// [self handleEndEditingForActiveAttribute];
//}
self.editedAttributeID = attributeID;
self.temporaryValue = nil;
}
-(void) handleValueChangeForEditedAttribute:(NSString *)newValue {
self.temporaryValue = newValue;
}
-(void) handleEndEditingForEditedAttribute {
if (self.temporaryValue != nil) { // Only if value has changed
NSLog(@"UIEventHandler:saveValue:%@:{%@}", self.editedAttributeID, self.temporaryValue);
// Causes the view to regenerate
// The UITextField loses first responder status and UIControlEventEditingDidEnd is gets triggered too late
[self.storage saveValue:self.temporaryValue
forAttribute:self.editedAttributeID];
self.temporaryValue = nil;
}
self.editedAttributeID = nil;
}
如果我理解正确,您遇到的问题是当文本字段是第一响应者时更改开关值,然后您的文本字段的文本将更新为开关的值。
A UITextField
的 didEndEditing:
事件仅在文本字段退出第一响应者时才会发生。如果您只想确保当开关值更改时文本字段结束编辑,您应该在开关收到 UIControlEventValueChanged
事件时将 endEditing:
消息发送到活动文本字段。
现在如何调用文本字段中的 endEditing:
消息取决于您的 classes 的结构。您可以在 table 视图单元格 class 上指定一个初始化方法,您可以在其中传递与控制文本字段的 UISwitch
相对应的 UITextField
实例。保留对文本字段实例的弱引用,然后在开关值更改时调用 endEditing:
。或者您可以简单地尝试在触发 switchChanged:
事件或 [self.superview endEditing:YES];
时在 UITableViewCellWithSwitch
上调用 [self endEditing:YES];
。我更喜欢前一种解决方案而不是后者,因为后者更像是一种黑客而不是正确的解决方案。
更新:
检查您的代码后,您在问题中提到的错误的原因
It leads to errors like this (value of switch displayed in text field):
是下面的一段代码:
- (void)switchChanged:(UISwitch *)sender {
NSLog(@"UISwitch:startEditing:%@",self.attributeID);
[self handleStartEditingForAttributeID:self.attributeID];
NSString* newValue = sender.on==YES?@"true":@"false";
NSLog(@"UISwitch:valueChanged:{%@}", newValue);
[self handleValueChangeForEditedAttribute:newValue];
NSLog(@"UISwitch:endEditing");
[self handleEndEditingForEditedAttribute];
}
您调用 handleValueChangeForEditedAttribute:
时使用的属性开关值实际上只应保存文本字段的值。在您的 UIEventHandler
class 中,您正在使用 handleEndEditingForEditedAttribute
方法中的开关值更新您的数据访问对象 (DAO)。将您的 switchChanged:
方法的逻辑更改为如下内容:
- (void)switchChanged:(UISwitch *)sender {
if (sender.on == YES) {
[self handleStartEditingForAttributeID:self.attributeID];
} else {
[self handleEndEditingForEditedAttribute];
}
}
并且在您的 UIEventHandler
class 中,取消注释 post 中的 "possible solution" 注释行。这应该确保在存储新 attributeID
.
-(void) handleStartEditingForAttributeID:(NSString *)attributeID {
// Possible solution
if (self.editedAttributeID != nil && [attributeID isEqualToString:self.editedAttributeID]==NO) { // Workaround needed for UISwitch events
[self handleEndEditingForActiveAttribute];
}
self.editedAttributeID = attributeID;
self.temporaryValue = nil;
}
我最好的解决方案是解决 UIEventHandler.m
中的问题。如果在调用 startEditing
时 endEditing
事件尚未触发,它会从 UIEventHandler
.
-(void) handleStartEditingForAttributeID:(NSString *)attributeID {
// Possible solution
if (self.editedAttributeID != nil && [attributeID isEqualToString:self.editedAttributeID]==NO) { // Workaround needed for UISwitch events
[self handleEndEditingForActiveAttribute];
}
self.editedAttributeID = attributeID;
self.temporaryValue = nil;
}
-(void) handleValueChangeForEditedAttribute:(NSString *)newValue {
self.temporaryValue = newValue;
}
-(void) handleEndEditingForEditedAttribute {
if (self.temporaryValue != nil) { // Only if value has changed
NSLog(@"UIEventHandler:saveValue:%@:{%@}", self.editedAttributeID, self.temporaryValue);
// Causes the view to regenerate
// The UITextField loses first responder status and UIControlEventEditingDidEnd is gets triggered too late
[self.storage saveValue:self.temporaryValue
forAttribute:self.editedAttributeID];
self.temporaryValue = nil;
}
self.editedAttributeID = nil;
}