Вызов -setNeedsDisplay:YES из -drawRect?

Я настраиваю свой метод drawRect:, который служит для рисования NSImage, если он был «загружен» (загрузка занимает несколько секунд, потому что я беру его из WebView), и откладываю рисование изображения на потом, если изображение еще не загружено.

- (void)drawRect:(NSRect)dirtyRect
{
    NSImage *imageToDraw = [self cachedImage];
    if (imageToDraw != nil) {
        [imageToDraw drawInRect:dirtyRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil];
    } else {
        //I need help here
        [self setNeedsDisplay:YES];
    }
}

Мой вопрос в том, как сделать последнее. [self cachedImage] возвращает nil, если изображение недоступно, но в любое время в течение следующих нескольких секунд оно может стать доступным, и в это время я хочу его нарисовать, потому что пользовательский вид уже на экране.

Моим первоначальным побуждением было попытаться вызвать [self setNeedsDisplay:YES];, если изображение было недоступно, в надежде, что он скажет Cocoa снова вызвать drawRect в следующий раз (и снова, и снова, и снова, пока изображение не будет нарисовано), но это не помогло. работай.

Любые указатели относительно того, куда я могу пойти отсюда?


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

Я очень хорошо осведомлен о методах делегата для WebView, которые срабатывают, когда loadRequest полностью обработан. Однако использовать их будет очень сложно из-за структуры остальной части приложения, но я думаю, что попытаюсь как-то использовать их сейчас, учитывая текущие ответы. (также обратите внимание, что мой метод drawRect: относительно легкий, в нем нет ничего, кроме кода, который у меня уже есть выше.)

В настоящее время у меня есть около 10+ пользовательских представлений, каждое из которых содержит настраиваемые данные, запрашивающие один и тот же WebView для создания изображений для каждого из них. В то же время я беру изображение из NSCache (используя идентификатор, соответствующий каждому пользовательскому представлению) и создаю его, если оно не существует или нуждается в обновлении, и возвращаю nil, если оно еще недоступно. Следовательно, это не так просто, как вызов [view setNeedsDisplay:YES] из - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame или другого метода.


person Vervious    schedule 12.05.2012    source источник
comment
Как именно вы захватываете этот образ?   -  person DrummerB    schedule 13.05.2012
comment
-drawRect: никогда не должно иметь побочных эффектов. Он должен рисовать только в соответствии с текущим состоянием представления и должен делать это максимально эффективно. Отправка сообщения -setNeedsDisplay: внутри -drawRect: приведет к тому, что ваше представление будет перерисовываться каждый раз, когда вы проходите основной цикл событий.   -  person NSResponder    schedule 13.05.2012


Ответы (3)


Первым моим побуждением было попробовать вызвать [self setNeedsDisplay:YES]; если изображение было недоступно, в надежде, что он скажет Cocoa снова вызвать drawRect в следующий раз (и снова, и снова, и снова, пока изображение не будет нарисовано)

Это было бы невероятно неэффективно, даже если бы работало.

в любое время в течение следующих нескольких секунд он может стать доступным, и в это время я хочу его нарисовать

Итак, когда это произойдет, позвоните [view setNeedsDisplay:YES].

Если у вас нет возможности напрямую определить, когда изображение станет доступным, вам придется провести опрос. Настройте повторяющееся NSTimer с разумным интервалом — скажем, 0,25 секунды или около того. (Это также довольно неэффективно, но, по крайней мере, оно работает только 4 раза в секунду вместо 60 или хуже. Это компромисс между двумя факторами: сколько процессора и мощности батареи вы хотите использовать, и как долго длится задержка между временем изображение становится доступным и время, когда вы его показываете.)

мой метод drawRect: относительно легкий, в нем нет ничего, кроме кода, который у меня уже есть выше.

Даже если вы вообще ничего не делаете в -drawRect:, Cocoa по-прежнему нужно выполнять много работы за кулисами — ему нужно управлять грязными прямоугольниками, очищать соответствующую область резервного хранилища окна, сбрасывать его на экран и т. д. Нет из этого бесплатно.

person Kurt Revis    schedule 12.05.2012
comment
Я думаю, что это плохое решение. WebView отправляет уведомления, когда происходят события, и должен быть реализован делегат, чтобы перехватывать эти события и вызывать [view setNeedsDisplay:YES]. Здесь нет необходимости в NSTimer! - person thundersteele; 13.05.2012
comment
Конечно. Вот что я имел в виду, когда это происходит: если есть метод делегата или уведомление, которое вы можете использовать, конечно используйте его. Если такого метода нет, вам придется сделать это менее эффективным способом. ОП не уточнил, в чем дело. - person Kurt Revis; 13.05.2012
comment
Истинный. Это помечено как какао, где такие уведомления отправляются WebView. В противном случае опрос, конечно, является жизнеспособным решением. - person thundersteele; 13.05.2012
comment
Ладно, раз ты так говоришь! Время реструктурировать мое приложение ~ спасибо. - person Vervious; 13.05.2012

Ну, обычно есть какой-то метод делегата, который вызывается, когда загрузка чего-то заканчивается. Вы должны реализовать этот метод и вызвать setNeedsDisplay:YES там.

person DrummerB    schedule 12.05.2012

Документация для вебкита:

https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DisplayWebContent/Tasks/ResourceLoading.html#//apple_ref/doc/uid/20002028-CJBEHAAG

Вы должны реализовать следующий метод в своем делегате веб-просмотра:

 - webView:resource:didFinishLoadingFromDataSource: 

Там вы можете позвонить [view setNeedsDisplay:Yes]

person thundersteele    schedule 12.05.2012