2011-10-04 131 views
6

我開發了注射系統,並且在Mac OS X上使用了一些石英API來創建一些很好的效果。例如,當用戶在窗口中設置顏色爲紅色時,它是紅色的有光澤的紅色。強制使用Core Graphics重繪自己的窗口?

但是,當我在已經運行的應用程序中注入時,由於窗口已經被繪製,我不能給它期望的效果。所以,我正在尋找石英/核心圖形的東西,這可以讓我重繪整個窗口或某種技術,可以讓我發送一些事件/調用一些功能,這將使系統重新繪製整個窗口。

我的意思是每個窗口上的東西都要再次繪製,這樣我的掛鉤API就會執行以創建適當的效果,陰影和顏色。這裏創建窗口的順序&繪製很重要。

我使用的技術類似於inject&interpose,注入代碼是C/C++代碼。

有沒有人有一個想法我怎麼能做到這一點?

+2

有沒有像Mac上的invalidateRect這迫使窗口重繪? – MacGeek

回答

5

-[NSView setNeedsDisplayInRect:]-[NSView setNeedsDisplay:]invalidateRect的直接等價物。

我不明白你在Quartz/CoreGraphics中是否需要它。可可已經在使用它們進行繪圖。

如果你想調用一些神奇的CGxxx()函數來重新繪製窗口,它不能完成。該窗口的標題和框架由系統繪製,但對於內容而言,低級別的API無法知道應該在那裏繪製哪些內容。唯一知道如何繪製視圖的人是視圖本身。 (也許有些東西被緩存在窗口的後臺存儲中,但我不知道任何公開或未公開的API訪問它)。

無論你發現只是基於要求NSWindow對象重新繪製其視圖。如果您已經注入過程中,可能包括以下步驟:

  • 定位OBJ-C運行時(你將需要至少objc_msgSend功能)
  • 使用+[NSApplication sharedApplication]-[NSApplication windows]定位的NSApplication類
  • 使用contentView找到NSWindow*對象指針
  • display等重繪
+0

對不起,我應該增加更多的細節。我想要石英的解決方案。 – MacGeek

+0

@MachinTosh我不知道我理解你,但看到我更新的答案。 – hamstergene

+0

您的回答爲解決方案提供了一個很好的方法。我正在尋找一些神奇的CGxxx(:D),因爲我正在嘗試在石英水平上,並且我試圖避免混合。 – MacGeek

5

如果您要求強制窗口使用比Cocoa更低級別的API重新繪製自己的窗口,那麼據我所知,這是不可能的。一個窗口在其內容視圖的drawRect:方法被調用時重繪自己。它將一個CGContextRef傳遞給窗口,然後該窗口使用該窗口重新繪製窗口。 CoreGraphics不負責重新繪製窗口。可可使用CoreGraphics重繪窗口。

它有可能獲得一個窗口的graphicscontext的drawRect之外:(見,例如,here),然後繪製到,只要你想, 但它聽起來像是你真的想要做的是攔截結果這個窗口的普通繪圖程序,並在頂部做一些你自己的東西。您可以通過切換窗口內容視圖的類並重寫drawRect來完成此操作。一個輔助函數來處理注入將是這個樣子:

typedef void (^InjectedBlock)(CGContextRef, CGRect); 

void InjectIntoView(NSView* view, InjectedBlock aBlock) 
{ 
    Class viewClass = [view class]; 
    InjectedBlock injectedBlock = [aBlock copy]; 

    void(^drawRect)(id, SEL, NSRect) = ^(id self, SEL _cmd, NSRect rect) 
    { 
     struct objc_super superId = { self, viewClass }; 
     objc_msgSendSuper(superId, @selector(drawRect:), rect); 

     injectedBlock([[NSGraphicsContext currentContext] graphicsPort], CGRectFromNSRect(rect)); 
    }; 

    NSString* subclassName = [NSString stringWithFormat:"%s_injected", class_getName(viewClass)] 
    Class subclass objc_allocateClassPair(viewClass, [subclassName UTF8String], 0); 
    objc_registerClassPair(subclass); 

    Method overriddenMethod = class_getInstanceMethod([NSView class], @selector(drawRect:)); 
    IMP imp = imp_implementationWithBlock(drawRect); 

    class_addMethod(subclass, @selector(drawRect:), imp, method_getTypeEncoding(overriddenMethod)) 
} 

編輯:

唉唉,你感興趣的整個窗口。框架等也是NSView實例,但它們是您無法直接訪問的NSView的私有子類。您可以通過在窗口上調用display來強制重繪它們,但這可能會覆蓋您對窗口所做的任何操作,因爲它將使用這些類的現有繪圖例程。

因此,您可能還需要考慮在drawRect中調用drawRect:這些視圖的方法(調用[[NSGraphicsContext currentContext] graphicsPort]:將爲您提供可用於Quartz API的CGContextRef)。您可以通過在窗口的內容視圖中調用superview來獲取框架視圖。

請注意,窗口框架視圖的排列沒有記錄,並可能隨系統更新而改變。

聽起來像一個有趣的項目,反正!

+0

我得到了答案的第一部分。但不是第二個。所以,CoreGraphics不會畫到窗口可可。很公平!但在第二部分中,您展示了令人興奮的(我不知道這種技術,謝謝)。然而,更適當的解決方案可能是一些如何將drawRect:]指令發送到窗口?我正在使用類似於'https://github.com/comex/inject_and_interpose/'的技術。所以它的C/C++代碼。你能提出一些與此相關的事嗎? – MacGeek

+0

我想我可能會誤解你在做什麼......也許你可以在你的問題中多說一些你對你注入的應用程序的窗口做了些什麼? –

+0

我已經更新了這個問題。 – MacGeek

3

我沒有遇到過使rect失效的事情,但是因爲你的問題是如何重畫一個完整的窗口,所以它似乎並不是你所需要的。

無效時,您告訴系統一部分視圖無效。下一次您的系統有時間進行繪圖(通常在聲明失效後立即),它會重繪您失效的矩形。

setNeedsDisplay的功能完全相同,除了整個視圖而不是該視圖內的特定矩形。在你的問題中,這並不重要,因爲你想刷新整個窗口。它從UIView鏈接到Quartz到內部系統,所以只要你通過繪圖時調用的drawRect來處理它,你的Quartz繪圖也會被使用。

所以只需調用[yourWindow setNeedsDisplay];系統會知道你的窗口需要儘快重新繪製。

相關問題