5

startDateとendDateを持つ(カレンダー)イベントを変更できるようにしたい。期間もあります。

  1. ユーザーがendDateを変更すると、期間が更新されます。
  2. ユーザーがstartDateを変更すると、期間に応じてendDateが変更されます。
  3. ユーザーが期間を変更すると、endDateが変更されます。

この最後のアクションは最初のアクションをトリガーし、3番目のアクションをトリガーし、最初のアクションを無限に(またはスタックがいっぱいになったときに)トリガーします。

次のような行は、値を変更するために、このループを引き起こします。

        [self setValue:[NSNumber numberWithLong:(interval%60)] forKeyPath:@"durationMinutes"];
        [self setValue:ed forKeyPath:@"endDate"];

単に監視を停止し、変更後に再開することは、GUIの値が更新されないため、魅力的ではありません。したがって、問題は、相互に依存する2つのプロパティのいずれかを安全に(そしてエレガントに)更新するにはどうすればよいかということです。

4

4 に答える 4

6

を使用して、必要に応じて KVO 通知をバイパスできますsetPrimitiveValue:forKey:。これにより値が設定されますが、通知はトリガーされません。また、プロパティに対して使用する可能性のあるカスタム セッターもバイパスします。それは呼び出しサイクルを壊すはずです。

このメソッドを使用する場合、通常は「will change」メソッドと「did change」メソッドを呼び出して、コア データの状態が維持されるようにします。つまり、次のようなものです。

[self willChangeValueForKey:@"endDate"];
[self setPrimitiveValue:ed forKey:@"endDate"];
[self didChangeValueForKey:@"endDate"];

これにより、フラグの必要がなくなります。値を設定するだけでいいので、いじる必要はありません。

于 2013-01-09T23:09:58.817 に答える
3

最善の方法は、値が変更された場合にのみ値を更新するようにカスタム セッターを作成することです。

例えば:

- (void)setDurationMinutes:(NSNumber *)minutes
{
    if (![minutes isEqual:self.durationMinutes])
    {
        _durationMinutes = minutes;
        self.endDate = [self.startDate dateByAddingTimeInterval:minutes * 60]; 
    }
}

- (void)setEndDate:(NSDate *)date
{
    if (![_endDate isEqualToDate:date])
    {
        _endDate = date;
        self.durationMinutes = [date timeIntervalSinceDate:self.startDate] / 60;
    }
}

したがって、分を別の値に設定すると、更新されて終了日が設定されます。終了日が異なるため、更新して期間を設定します。ただし、今回は (以前に設定されていたため) 期間は同じであり、それ自体または終了日を再度設定しようとはしません。

また、コードを適切かつクリーンに保ち、何がいつ何を変更するかを明確にします。

于 2013-01-19T18:03:24.363 に答える
1

3 つすべてを 1 つのメソッドに結合します。

- (void)updateEndDate:(NSDate *)end
            startDate:(NSDate *)start
             duration:(NSNumber *)duration
{
  ... insert your update logic here
  [self willChangeValueForKey:@"startDate"];
  [self willChangeValueForKey:@"endDate"];
  [self willChangeValueForKey:@"duration"];
  _endDate = end;
  _startDate = start;
  _duration = duration;
  [self didChangeValueForKey:@"duration"];
  [self didChangeValueForKey:@"endDate"];
  [self didChangeValueForKey:@"startDate"];
}

セッターでそのメソッドを呼び出します

-(void)setStartDate:(NSDate *)start
{
   [self updateEndDate:self.endDate startDate:start duration:self.duration];
}

KVO コンプライアンスでは、プロパティの通知を手動で送信することを指定する必要があります

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey
{

    BOOL automatic = NO;
    NSArray * manualKeys = @[@"endDate", @"startDate", @"duration"];
    if ([manualKeys containsObject:theKey])
    {
        automatic = NO;
    }
    else
    {
        automatic = [super automaticallyNotifiesObserversForKey:theKey];
    }
    return automatic;
}
于 2013-01-09T22:02:23.830 に答える
0

結局、私はシステムと戦っていたことがわかりました。ここで、期間の値をその場で計算し、IBActionsを使用して変更に対応します。

- (IBAction)changedEndDate:(id)sender
    if ( [_occurrence.endDate timeIntervalSinceDate:_occurrence.startDate] < 0 )
            _occurrence.startDate = _occurrence.endDate;

    long duration = ( [_occurrence.endDate timeIntervalSinceDate:_occurrence.startDate] ) / 60;
    [self.durationHoursTextField setStringValue:[NSString stringWithFormat:@"%ld",duration/60]];
    [self.durationMinutesTextField setStringValue:[NSString stringWithFormat:@"%ld",duration%60]];
}
- (IBAction)changedDurationOrStartDate:(id)sender
{
    long durH = [self.durationHoursTextField integerValue];
    long durM = [self.durationMinutesTextField integerValue];
    if ( durH < 0 )
    {
        durH = 0;
        [self.durationHoursTextField setStringValue:@"0"];
    }
    if ( durM < 0 )
    {
        durM = 0;
        [self.durationMinutesTextField setStringValue:@"0"];
    }
    long duration = durH * 3600 + durM * 60;
    _occurrence.endDate = [_occurrence.startDate dateByAddingTimeInterval:duration];
 }
于 2013-01-19T17:07:19.460 に答える