Обвързването на стойност на плъзгач не се актуализира правилно

приложението, с което работя в момента, контролира позицията във филм QuickTime с нормален NSSlider. Точно както във всеки медиен плейър там.

Исках да свържа стойността на плъзгача с позицията на филма (която е в диапазон от 0 до 1), за да избегна залепващ/залепващ код.

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

По някаква причина това все още не работи. Също така имам някои отчети за журнал в getter. Не е извикан дори веднъж!

Не мога да си обясня каква може да е причината за това поведение. Единственото нещо, което е малко по-различно от простите примери, намерени онлайн (и в книгата на Арън Хилегас), е, че използвам ключов път вместо обикновен ключ.

От IB отивам в App Delegate, който има свойство activeMovie. ActiveMovie е екземпляр на клас VS_Movie, който има свойство moviePosition. Така че моят ключов път е: self.activeMovie.moviePosition, който Xcode също изглежда намира.

Ще бъда благодарен за всякакви съвети и помощ! Благодаря предварително

Ето малко код, който използвам:

1. Декларация за имущество

// ivar
NSNumber* _moviePosition;

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

2. Реализация на getter и setter

- (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
Ключовият път не трябва да започва със себе си, но не знам дали това е проблемът. Покажете декларациите на свойствата и, ако не са синтезирани, имплементациите както за 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 се променя всеки път, когато се отвори нов филм. Свързването се установява с KVO, нали? Но какво се случва, когато наблюдаваният обект (един компонент на пътя на ключа) се промени? - 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