Значение UISwitch изменило срабатывание события до завершения редактирования UITextField - PullRequest
0 голосов
/ 20 ноября 2018

В моем приложении много UISwitch э и UITextField с отображаются в списке UITableViewCell с.

Когда пользователь начинает редактировать UITextField, а затем нажимает на UISwitch, порядок событий заставляет UITextField отображать значение UISwitch, поскольку обработчик событий не получилЗавершить редактирование события UITextField.

Как надежно гарантировать, что событие UIControlEventEditingDidEnd для UITextField будет запущено до UIControlEventValueChanged из UISwitch?

Это приводит к таким ошибкам (значение переключателяотображается в текстовом поле):

UISwitch and UITextField

Шаги (что должно произойти):

1. Нажмите UISwitch, чтобы активировать его

UISwitch:startEditing:switch243
UISwitch:valueChanged:{true}
UISwitch:endEditing
UIEventHandler:saveValue:switch243:{true}

2. Нажмите UITextField, чтобы начать его редактирование

UITextField:startEditing:textfield455

3. Нажмите 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, который объединяет все события редактирования пользовательского интерфейса:

-(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;
}

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Моим лучшим решением еще было решить проблему в 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;
}
0 голосов
/ 20 ноября 2018

Если я правильно понимаю, проблема заключается в том, что при изменении значения переключателя, когда текстовое поле является первым респондентом, текст вашего текстового поля обновляется до значения переключателя.

A UITextField Событие didEndEditing: происходит только в том случае, если текстовое поле подает в отставку первый респондент.Если все, что вам нужно сделать, это убедиться, что текстовое поле завершает редактирование при изменении значения переключателя, вы должны отправить сообщение endEditing: в активное текстовое поле, когда коммутатор получает событие UIControlEventValueChanged.

Сейчасспособ вызова сообщения endEditing: в текстовом поле зависит от структуры ваших классов.Вы можете иметь назначенный метод инициализатора в своем классе ячеек табличного представления, где вы передаете экземпляр UITextField, соответствующий UISwitch, который контролирует текстовое поле.Сохраните слабую ссылку на экземпляр текстового поля и затем вызовите endEditing:, когда значение переключателя изменится.Или вы можете просто попытаться вызвать [self endEditing:YES]; на UITableViewCellWithSwitch, когда происходит событие switchChanged: или [self.superview endEditing:YES];.Я предпочитаю первое решение, а не второе, поскольку последнее скорее взломано, чем правильное решение.

ОБНОВЛЕНИЕ:

После просмотра вашего кода причинаошибка, о которой вы упоминали в своем вопросе

Это приводит к таким ошибкам (значение переключателя, отображаемое в текстовом поле):

- это следующий фрагмент кода:

- (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 вы обновляете свой объект доступа к данным (DAO) значением переключателя в методе handleEndEditingForEditedAttribute.Измените логику метода switchChanged: на что-то вроде этого:

- (void)switchChanged:(UISwitch *)sender {
    if (sender.on == YES) {
        [self handleStartEditingForAttributeID:self.attributeID];
    } else {
        [self handleEndEditingForEditedAttribute];
    }
}

И в своем классе UIEventHandler раскомментируйте закомментированные строки в своем посте, которые говорят «возможное решение».Это должно обеспечить сохранение всех предыдущих изменений перед сохранением значений для нового 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;
}
...