Пользовательские NSStatusItem и NSView не могут надежно получать NSTrackingEvents

У меня есть приложение только для элемента строки состояния, которое я пытаюсь заставить отображать панель при наведении курсора мыши. У меня есть настраиваемый элемент статуса (и связанное с ним представление), подключенный и работающий, но прямоугольник отслеживания получает события только при каждой дюжине или около того запусков. Это заставляет меня поверить, что где-то происходит состояние гонки, но я не могу его найти. В моем пользовательском представлении элемента строки состояния:

- (id)initWithStatusItem:(NSStatusItem *)statusItem {
    CGFloat itemWidth = [statusItem length];
    CGFloat itemHeight = [[NSStatusBar systemStatusBar] thickness];
    NSRect itemRect = NSMakeRect(0.0, 0.0, itemWidth, itemHeight);
    NSLog(@"itemRect: %@", NSStringFromRect(itemRect));

    if ((self = [super initWithFrame:itemRect])) {
        _statusItem = statusItem;
        _statusItem.view = self;

        NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways;
        NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:itemRect
                                                                    options:options
                                                                      owner:self
                                                                   userInfo:nil];
        [self addTrackingArea:trackingArea];

        [self.window setIgnoresMouseEvents:NO];
        [self.window setAcceptsMouseMovedEvents:YES];

        self.wantsLayer = YES;
    }
    return self;
}

- (void)mouseEntered:(NSEvent *)theEvent {
    [[NSNotificationCenter defaultCenter] postNotificationName:UAStatusItemMouseEnteredNotification object:nil];
}

- (void)mouseExited:(NSEvent *)theEvent {
    [[NSNotificationCenter defaultCenter] postNotificationName:UAStatusItemMouseExitedNotification object:nil];
}

При большинстве запусков приложение не реагирует на отслеживание событий мыши, но время от времени методы mouseEntered: и mouseExited: вызываются правильно, что полностью сбивает меня с толку. Что здесь происходит и что я делаю неправильно?




EDIT 17/07/2012
Я изменил код на основе ответа @Streams, но увидел ту же проблему:

- (id)initWithStatusItem:(NSStatusItem *)statusItem {
    CGFloat itemWidth = [statusItem length];
    CGFloat itemHeight = [[NSStatusBar systemStatusBar] thickness];
    NSRect itemRect = NSMakeRect(0.0, 0.0, itemWidth, itemHeight);
    NSLog(@"itemRect: %@", NSStringFromRect(itemRect));

    if ((self = [super initWithFrame:itemRect])) {
        _statusItem = statusItem;
        _statusItem.view = self;            

        [self updateTrackingAreas];

        [self.window setIgnoresMouseEvents:NO];
        [self.window setAcceptsMouseMovedEvents:YES];

        self.wantsLayer = YES;
    }
    return self;
}

- (void)updateTrackingAreas {

    if (self.trackingArea)
        [self removeTrackingArea:self.trackingArea];

    [super updateTrackingAreas];

    self.trackingArea = [[NSTrackingArea alloc] initWithRect:CGRectZero
                                                     options:NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingInVisibleRect | NSTrackingActiveAlways
                                                       owner:self
                                                    userInfo:nil];
    [self addTrackingArea:self.trackingArea];
}




РЕДАКТИРОВАТЬ 18.07.2012
Вот базовая версия пример проекта, который использует хорошо известный проект github (написанный @Stream) для демонстрации проблемы. Он не может надежно получать события наведения мыши, если вообще получает.


person coneybeare    schedule 17.07.2012    source источник
comment
Как ни странно, пример проекта отлично работает в моей системе.   -  person Vervious    schedule 20.07.2012
comment
@Vervious Оно отлично работает при каждом запуске приложения?   -  person coneybeare    schedule 20.07.2012


Ответы (2)


Я открыл запрос DTS, чтобы Apple взглянула на это. Вот ответ:

... вы используете полноэкранный режим в Xcode при запуске приложения. Я не делал этого [раньше], но теперь я могу воспроизвести проблему. Из того, что я могу сказать, это происходит только тогда, когда ваше приложение запускается из полноэкранного режима в Xcode. Ваши пользователи не будут запускать приложение таким образом. Это проблема с полноэкранным режимом AppKit, а не обязательно с вашим кодом.

person coneybeare    schedule 23.07.2012
comment
У меня та же проблема, которую я могу воспроизвести при запуске моего приложения, когда я нахожусь в полноэкранном режиме. НО, он все еще иногда не работает, хотя я не нахожусь в полноэкранном режиме во время запуска. Похоже, это то же самое для вас? Вам удалось это решить? - person Sebastian; 19.04.2013

Я считаю, что вы должны управлять областями отслеживания только в -[NSView updateTrackingAreas]. Например:

- (void)updateTrackingAreas
{
    if (_trackingArea) {
        [self removeTrackingArea:_trackingArea];
    }

    [super updateTrackingAreas];

    NSTrackingAreaOptions options = (NSTrackingMouseEnteredAndExited |
                                     NSTrackingMouseMoved |
                                     NSTrackingInVisibleRect |
                                     NSTrackingActiveAlways);
    _trackingArea = [[NSTrackingArea alloc] initWithRect:CGRectZero
                                                 options:options
                                                   owner:self
                                                userInfo:nil];
    [self addTrackingArea:_trackingArea];
}
person Vadim    schedule 17.07.2012
comment
Для представления, которое не меняет размер, это нормально в init, но ключ к ответу в любом случае лежит здесь! Изменение моей переменной rect на CGRectZero, как у вас здесь, заставляет ее работать каждый раз. Согласно документам, прямоугольник находится в координатном пространстве получателя, поэтому я отправлял его с помощью self.bounds. Я не знаю, почему это не сработало, но CGRectZero — это правильный ответ. Вы были более чем полезны :) - person coneybeare; 18.07.2012
comment
Похоже, я заговорил слишком рано. Вроде тоже с перерывами. - person coneybeare; 18.07.2012
comment
Я обновил с новым кодом в соответствии с вашей рекомендацией. До сих пор не удалось воспроизвести результат после первых 3 попыток, прежде чем написать первый комментарий. - person coneybeare; 18.07.2012
comment
В качестве альтернативы вы можете настроить монитор событий, чтобы отслеживать, когда положение мыши находится в пределах вашего представления состояния. Даже обычные NSTimer с +[NSEvent mouseLocation] могут работать. - person Vadim; 18.07.2012
comment
Это так странно, потому что каждый десяток или около того запускает приложение, которое отлично работает. Этот код работает именно так, как задумано. Как вы думаете, может ли строка состояния иметь какое-то внутреннее кэширование, которое каким-то образом мешает применению NSTrackingRect? - person coneybeare; 18.07.2012
comment
Я добавил пример проекта с этим кодом в ваш базовый репозиторий всплывающих окон. github.com/shpakovski/Popup - person coneybeare; 18.07.2012
comment
Зарегистрируйте радар или опубликуйте этот вопрос на форумах разработчиков Apple, если ничего не помогает :) - person Vadim; 18.07.2012
comment
updateTrackingRect не требуется при использовании NSTrackingInVisibleRect, поскольку размер никогда не меняется. Этот ответ здесь неприменим. - person coneybeare; 24.07.2012