Пользовательское представление NSMenuItem не обновляется

У меня есть объект NSStatusItem, который создается при запуске приложения (в AppDelegate.m). К этому объекту «_statusBarItem» прикреплено меню класса statusBarMenu, которое является подклассом класса NSMenu, но у него также есть свойство _panelItem (класса NSMenuItem), созданное при создании экземпляра statusBarMenu, как вы можете видеть ниже.

В statusBarItem.m

- (instancetype)initWithTitle:(NSString *)aTitle{
self = [super initWithTitle:aTitle];

if (self){

    _panelItem = [[NSMenuItem alloc]init];
    PanelViewController *panelViewController = [[PanelViewController alloc]init];
    panelViewController.menuDelegate = self;
    _panelItem.view = panelViewController.view;

    [self addItem:_panelItem];
}
return self;
}

_PanelItem имеет настраиваемый вид, то есть часы в метке (среди прочего). Это представление контролируется классом PanelViewController, в котором при вызове метода viewDidLoad вызывается метод tickTock :, показанный ниже. _upTime - это метка, показывающая часы / время. Он создается в файле .xib и подключается

- (void)tickTock:(id)obj{
NSTimeInterval timeInterval = 7.5 * 60 * 60;

NSDate *timeToGetUp = [NSDate dateWithTimeInterval:timeInterval sinceDate:[NSDate date]];

NSDateFormatter *dateFormatter = [NSDateFormatter new];
[dateFormatter setDateFormat:@"hh:mm:ss"];

[_upTime setStringValue:[dateFormatter stringFromDate:timeToGetUp]];

[self.view setNeedsDisplay:YES];
[self.menuDelegate refreshView:self];

[self performSelector:@selector(tickTock:) withObject:NULL afterDelay:1.0];
}

Как видите, метод tickTock: вызывается каждые 1,0 секунды. Это потому, что я хочу, чтобы метка обновлялась каждую секунду с новым временем. Однако метка не обновляется, хотя я вызываю setNeedsDisplay: для представления PanelViewController. Я подумал, что это могло быть потому, что я мог обновлять неправильное представление, т.е. мне нужно было вместо этого обновлять представление _panelItem. Итак, я создал свойство menuDelegate и сделал statusBarMenu соответствующим протоколу, показанному ниже.

@protocol PanelViewControllerMenuDelgate

- (void)refreshView:(id)obj;

@end

Опять же, метод refreshView: вызывается каждую секунду, он обновляет представление панели.

- (void)refreshView:(id)obj{
[_panelItem.view setNeedsDisplay:YES];
//    [self itemChanged:_panelItem];
}

Однако это по-прежнему не обновляет представление и не отображает новое значение метки. Я также попробовал метод itemChanged: для самого объекта statusBarMenu (_statusBarItem), хотя он не дал других результатов.

Что я заметил, так это то, что если я закрою меню (щелкнув в другом месте) и снова открою его, оно покажет новое значение часов / метки. Итак, что я делаю не так, или что я не делаю, что заставляет представление оставаться прежним. Должен ли я использовать какую-то другую процедуру, чтобы представление _panelItem обновлялось каждую секунду?

РЕДАКТИРОВАТЬ:

Я также заметил, что симуляция перестает работать, когда каждый щелчок по _statusBarItem. Я просто добавил оператор NSLog в метод tickTock:, и он перестал печатать в консоли, когда я открываю меню. Вероятно, поэтому представление не обновляется, поскольку приложение / Xcode приостанавливается, когда я нажимаю на меню. Почему так случилось? Также как я могу это остановить?


person user14492    schedule 18.11.2014    source источник


Ответы (1)


Я столкнулся с этой проблемой несколько недель назад. В моем случае мой NSTimer не обновлял мой NSMenuItem. Я считаю, что ваша проблема возникает из-за того, что меню обновляется в другом цикле выполнения для выполнения с задержкой. Ответ на этот вопрос содержится в нескольких places. Но это код, который вам нужен:

NSTimer interfaceUpdateTimer = [NSTimer timerWithTimeInterval:self.timerSettings.timerUpdateInterval//one second in your case
                                         target:self
                                                  selector:@selector(tickTock:)
                                       userInfo:nil
                                        repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:interfaceUpdateTimer forMode:NSRunLoopCommonModes];

Ключ NSRunLoopCommonModes. Ознакомьтесь с другими ответами, на которые я ссылался; они уже очень хорошо это объясняют. Думаю, это должно помочь.


В качестве примечания: ни NSTimer, ни performSelector:withObject:afterDelay: не гарантированно сработают точно в указанное вами время. Поэтому, если вам нужна точность, не полагайтесь на них, чтобы определить время. Если достаточно близко, то вы можете проигнорировать это примечание :)

person lindon fox    schedule 15.12.2014