Привязка значения к ползунку не обновляется должным образом

приложение, над которым я сейчас работаю, контролирует положение в фильме QuickTime с помощью обычного NSSlider. Как и в любом другом медиаплеере.

Я хотел привязать значение ползунка к положению фильма (которое находится в диапазоне от 0 до 1), чтобы избежать липкого/клеевого кода.

Когда я перетаскиваю ползунок в пользовательском интерфейсе, вызывается правильный установщик, но по какой-то причине ползунок не обновляется, когда я изменяю значение программно. Я бы сказал, что понимаю принципы привязки и то, как работает KVC. И я также знаю, что вы должны либо использовать геттеры, либо сеттеры, либо уведомлять наблюдателей с помощью [self willChangeValueForKey:@"theKey"] и [self didChangeValueForKey:@"theKey"] при программном изменении значений.

Почему-то это все еще не работает. У меня также есть несколько операторов журнала в геттере. Его даже не называют ни разу!

Я не могу себе объяснить, в чем может быть причина такого поведения. Единственное, что немного отличается от простых примеров, найденных в Интернете (и в книге Аарона Хиллегасса), заключается в том, что я использую ключевой путь вместо простого ключа.

Из IB я перехожу к App Delegate, у которого есть свойство activeMovie. ActiveMovie — это экземпляр класса VS_Movie со свойством moviePosition. Итак, мой ключевой путь: self.activeMovie.moviePosition, который, похоже, также находит Xcode.

Буду признателен за любые подсказки и помощь! заранее спасибо

Вот код, который я использую:

1. Декларация собственности

// ivar
NSNumber* _moviePosition;

// Property
@property (retain) NSNumber* moviePosition;

2. Реализация геттера и сеттера

- (NSNumber*) moviePosition
{
    NSLog(@"MoviePosition returned : %lf", [_moviePosition doubleValue]);
    return _moviePosition;
}

- (void) setMoviePosition:(NSNumber*)newPos
{
    NSLog(@"New MoviePosition : %lf", [newPos doubleValue]);

    if ( _moviePosition && [newPos isEqualToNumber:_moviePosition] )
        return;

    if ( _moviePosition ) 
       [_moviePosition release];

    _moviePosition = [newPos retain];

    if ( _positioningReady )
    {
        _positioningReady = NO;

        TimeValue newMovieTime = (TimeValue)([_moviePosition doubleValue] * 
                                             (double)_clipDurationTimeValue);
        SetMovieTimeValue(_qtMovie, newMovieTime);
        [_movieView displayCurrentFrameOfMovie:self];

        // Post a notification to inform the observing 
        // classes so they can do the neccessary handling.
        NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
        [nc postNotificationName:VS_MoviePositionChangedNotification 
                      object:self];
    }
}

3. Функция вызова

- (void) setTimeValue:(TimeValue) value withTimeScale:(TimeScale) scale
{
    SetMovieTimeValue(_qtMovie, value);
    SetMovieTimeScale(_qtMovie, scale);

    double newMoviePosition = (double)GetMovieTime(_qtMovie, NULL) / (double)_clipDurationTimeValue;
    [self setMoviePosition:[NSNumber numberWithDouble:newMoviePosition]];

    [_movieView displayCurrentFrameOfMovie:self];
}

person guitarflow    schedule 17.05.2012    source источник
comment
Ключевой путь не должен начинаться с self., но я не знаю, в чем проблема. Покажите объявления свойств и, если они не синтезированы, реализации как для activeMove в классе делегатов приложения, так и для moviePosition в классе VS_Movie. Кроме того, что вы на самом деле используете, сеттеры или -will/DidChangeValueForKey:? Если последнее, покажите код, который это делает. Обновляется ли позиция в фоновом потоке? Наконец, почему вы не используете встроенные представления видеоплеера/контроллера?   -  person Ken Thomases    schedule 18.05.2012
comment
Я создаю приложение для профессионального использования. Встроенные просмотры видеоплеера/контроллера просто не предлагают то, что мне нужно. Обновлю пост и добавлю код.   -  person guitarflow    schedule 18.05.2012


Ответы (1)


Учитывая, что свойство называется moviePosition, геттер должен называться -moviePosition (без подчеркивания), а сеттер должен называться -setMoviePosition: (без подчеркивания и первая буква имени свойства заглавная).

Вы не показали декларацию @property. Хотя разрешено указывать имена сеттера и геттера, ни KVC, ни KVO не обращают на это внимания. Для них это должно следовать соглашениям об именах. См. Руководство по программированию кодирования ключ-значение: Шаблоны поиска доступа для простых атрибутов.

Вы уверены, что правильный сеттер вызывается, как вы утверждаете? Поскольку вы неправильно назвали свои методы доступа, компилятор не распознает их как таковые. Следовательно, @synthesize генерирует файлы с правильными именами. Они вызываются KVC (и перехватываются KVO).

Вот почему -setTimeValue:withTimeScale: избегает вызова [self setMoviePosition:newMoviePosition], который не соответствует вашему методу -set_moviePosition:. Однако вы должны получить предупреждение (а во время выполнения - сбой), потому что вы передаете double сеттеру, который ожидает NSNumber*.

person Ken Thomases    schedule 18.05.2012
comment
Мне так жаль. При копировании кода из Xcode я внес небольшие изменения и не исправил код проб и ошибок. Пожалуйста, проверьте обновленный код выше. Тем временем я прочитаю документ по предоставленной ссылке. Большое спасибо! - person guitarflow; 18.05.2012
comment
Новый код выглядит намного лучше, но теперь мне интересно, отражает ли он ситуацию, вызвавшую странное поведение, о котором вы спрашиваете. Возможно, вы непреднамеренно исправили проблему. Проблемы, которые я сейчас вижу: @synthesize является избыточным, поскольку вы предоставляете реализации средств доступа. В имени геттера есть обратная кавычка (`). Управление памятью в сеттере немного отключено. Вы не защищаете от того, что newPos совпадает с _moviePosition. Вы должны сохранить newPos перед выпуском _moviePosition или заключить весь метод в скобки if (newPos != _moviePosition) .... - person Ken Thomases; 18.05.2012
comment
Хорошо, исправлена ​​ошибка с памятью и удалена директива @synthesize. Спасибо за подсказку. К сожалению, это ничего не изменило. NSSlider должен получить уведомление, когда я изменяю код с помощью метода доступа, а затем вызываю установщик, чтобы получить новое значение, верно? Я просто не понимаю, почему это не работает. - person guitarflow; 18.05.2012
comment
Есть ли шанс, что у вас есть два отдельных экземпляра класса делегата приложения? Возможно, один экземпляр создан в NIB, а другой — в коде? - person Ken Thomases; 18.05.2012
comment
Нет. Но я только что понял, что экземпляр activeMovie меняется каждый раз, когда открывается новый фильм. Привязка установлена ​​с КВО, да? Но что происходит, когда наблюдаемый объект (один из компонентов ключевого пути) изменяется? - person guitarflow; 18.05.2012
comment
Вот оно! Привязывал свойство программно каждый раз, когда загружался новый фильм, и теперь он работает. Кажется, что наблюдатель получает наблюдаемый объект один раз, когда привязка установлена, но не обновляет его. Когда сеттер вызывается, он должен получить все текущие объекты (проходя по ключевому пути), но, к сожалению, он не проверяет, совпадают ли они с наблюдаемыми. Хорошо знать. Спасибо за вашу помощь! (Хотя ваш ответ на самом деле не решил проблему, я тем не менее отмечу его как правильный. Может быть, вы хотите немного его обновить?) - person guitarflow; 18.05.2012
comment
KVO будет правильно отслеживать изменения свойства на ключевом пути при условии, что свойство было обновлено в соответствии с KVO. Тот факт, что вам приходится (повторно) привязываться вручную при изменении activeMovie, говорит о том, что вы не меняете его в соответствии с KVO. - person Ken Thomases; 19.05.2012
comment
Вы были совершенно правы! Когда я изменил activeMovie, я напрямую изменил переменную экземпляра... спасибо, что пролили свет на это! - person guitarflow; 19.05.2012