QuickLook 使用者作为 NSViewController 的委托

发布于 2024-09-25 12:11:51 字数 4264 浏览 7 评论 0原文

我在从 NSView 中的表格实现 QuickLook 功能时遇到一些问题。 QuickLook 上的有限文档确实没有任何帮助。

阅读Apple 文档后(主要面向自定义生成器和插件),我最终查看了 QuickLookDownloader 示例代码。该代码基于基于文档的应用程序,但对我来说似乎是正确的方法(毕竟它是Apple的代码并且它确实在他们的项目中工作)。

在我的实现中,我可以让 QuickLook 面板 正常显示,并且我可以轻松地将其关闭。但是,面板本身永远不会从我的 NSViewController 中调用委托方法。结果,我什至从来没有显示过对象,只是显示“没有选择任何项目”的措辞。我很困惑。

我尝试调用 setDelegate,但如果我继续沿着这条路线走下去,就会收到关于即将到来的厄运的警告......

[QL] QLError(): -[QLPreviewPanel setDelegate:] 在面板没有控制器时调用 - 修复此问题,否则很快就会引发此问题。 请参阅 QLPreviewPanel.h 中 -acceptsPreviewPanelControl:/-beginPreviewPanelControl:/-endPreviewPanelControl: 的注释。

然后,当尝试响应委托方法之一时,无论如何都会发生dealloc 的厄运。

是的,我确实阅读了标题,它确认我应该在赢得小组后设置委托(请参阅下面的代码)。

这是我的代码,它与示例代码几乎匹配,除了 a) 我从中获取数据的地方(我从 NSArrayController 获取它)和 b) 我从其中获取预览项目的地方(我的直接来自我的模型对象 - 或者无论如何)

@interface MyViewController : NSViewController 
    <QLPreviewPanelDataSource, QLPreviewPanelDelegate> {

    QLPreviewPanel * previewPanel;
    NSArrayController * myArrayController;
    NSTableView * myTable;

    // [...] Other instance vars
}

@implementation MyViewController

// [...] all the other methods, init, dealloc etc...

-(IBAction)togglePreviewPanel:(id)previewPanel {

    if ([QLPreviewPanel sharedPreviewPanelExists] && 
          [[QLPreviewPanel sharedPreviewPanel] isVisible])
    {
       [[QLPreviewPanel sharedPreviewPanel] orderOut:nil];
    }
    else
    {
       [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
    }
 }

 -(BOOL)acceptsPreviewPanelControl:(QLPreviewPanel *)panel
 {    
      return YES;
 }

 // This document is now responsible of the preview panel. 
 // It is allowed to set the delegate, data source and refresh panel.

 -(void)beginPreviewPanelControl:(QLPreviewPanel *)panel 
 {

    if (DEBUG) NSLog(@"QuickLook panel control did BEGIN");

    previewPanel = [panel retain];
    panel.delegate = self;
    panel.dataSource = self;
 }

 // This document loses its responsisibility on the preview panel. 
 // Until the next call to -beginPreviewPanelControl: it must not change 
 // the panel's delegate, data source or refresh it.

 -(void)endPreviewPanelControl:(QLPreviewPanel *)panel   
 {
     [previewPanel release];
     previewPanel = nil;

     if (DEBUG) NSLog(@"QuickLook panel control did END");
 }

 // Quick Look panel data source

 -(NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel *)panel 
 {

     if (DEBUG) NSLog(@"QuickLook preview count called");

     return [[myArrayController selectedObjects] count];
 }

 -(id <QLPreviewItem>)previewPanel:(QLPreviewPanel *)panel
        previewItemAtIndex:(NSInteger)index
 {

     if (DEBUG) NSLog(@"QuickLook preview selection of item called");

     return [[displayAC selectedObjects] objectAtIndex:index];
 }

 -(BOOL)previewPanel:(QLPreviewPanel *)panel handleEvent:(NSEvent *)event {

    if (DEBUG) NSLog(@"QuickLook panel error handler called");

// redirect all key down events to the table view

    if ([event type] == NSKeyDown) {
        [myTable keyDown:event];
    return YES;
    }

    return NO;
}

问题似乎是acceptsPreviewPanelControl永远不会被调用,因此委托永远不会被使用(它们肯定永远不会被调用)。

我确信这是我缺少的一个简单步骤,但在剖析示例代码并搜索文档后,我没有看到答案。

是因为这一切都来自 NSViewController 内部(尽管我看不出为什么这应该进入等式)?

非常感谢任何和所有的帮助。

解决方案更新

由于 Peter 的观察,修复速度很快。当调试器中的错误消息如其所言时,您不讨厌它吗? :-)

在加载 MyViewController 的类中,我只需要添加三行代码即可解决问题。

// mainWindow is an IBOutlet to my window because the calling class 
// is a simple object and not an NSWindowController otherwise I could
// have used `self` instead of `mainWindow`

NSResponder * aNextResponder = [mainWindow nextResponder];

[mainWindow setNextResponder:myViewControllerInstance];
[myViewControllerInstance setNextResponder:aNextResponder];

工作完成:-) 谢谢彼得。

I am having some problems implementing QuickLook functionality from a table in an NSView. The limited documentation on QuickLook really doesn't help at all.

After reading through the Apple Docs (which are geared heavily towards custom generators and plugins), I ended up looking at the QuickLookDownloader sample code. This code is based upon a document-based application, but appears to be the right method for me (after all it is Apple's code and it does work in their project).

In my implementation I can get the QuickLook panel to show up just fine, and I can dismiss it just as easy. However, the panel itself never calls the delegate methods from within my NSViewController. As a result I never even get to displaying objects, just the wording "No items selected". And I am stumped.

I tried calling a setDelegate, but get warned about impending doom if I continue down that route...

[QL] QLError(): -[QLPreviewPanel setDelegate:] called while the panel has no controller - Fix this or this will raise soon.
See comments in QLPreviewPanel.h for -acceptsPreviewPanelControl:/-beginPreviewPanelControl:/-endPreviewPanelControl:.

And then doom happens anyway with a dealloc when trying to respond to one of the delegate methods.

And yes I did read the header which confirms that I should be setting the delegate after I won the panel (see code below).

So here's my code, which pretty much matches the sample code with the exception of a) where I get my data from (I get it from an NSArrayController) and the b) where I get my preview item from (mine comes directly from my model object - or should anyway)

@interface MyViewController : NSViewController 
    <QLPreviewPanelDataSource, QLPreviewPanelDelegate> {

    QLPreviewPanel * previewPanel;
    NSArrayController * myArrayController;
    NSTableView * myTable;

    // [...] Other instance vars
}

@implementation MyViewController

// [...] all the other methods, init, dealloc etc...

-(IBAction)togglePreviewPanel:(id)previewPanel {

    if ([QLPreviewPanel sharedPreviewPanelExists] && 
          [[QLPreviewPanel sharedPreviewPanel] isVisible])
    {
       [[QLPreviewPanel sharedPreviewPanel] orderOut:nil];
    }
    else
    {
       [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
    }
 }

 -(BOOL)acceptsPreviewPanelControl:(QLPreviewPanel *)panel
 {    
      return YES;
 }

 // This document is now responsible of the preview panel. 
 // It is allowed to set the delegate, data source and refresh panel.

 -(void)beginPreviewPanelControl:(QLPreviewPanel *)panel 
 {

    if (DEBUG) NSLog(@"QuickLook panel control did BEGIN");

    previewPanel = [panel retain];
    panel.delegate = self;
    panel.dataSource = self;
 }

 // This document loses its responsisibility on the preview panel. 
 // Until the next call to -beginPreviewPanelControl: it must not change 
 // the panel's delegate, data source or refresh it.

 -(void)endPreviewPanelControl:(QLPreviewPanel *)panel   
 {
     [previewPanel release];
     previewPanel = nil;

     if (DEBUG) NSLog(@"QuickLook panel control did END");
 }

 // Quick Look panel data source

 -(NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel *)panel 
 {

     if (DEBUG) NSLog(@"QuickLook preview count called");

     return [[myArrayController selectedObjects] count];
 }

 -(id <QLPreviewItem>)previewPanel:(QLPreviewPanel *)panel
        previewItemAtIndex:(NSInteger)index
 {

     if (DEBUG) NSLog(@"QuickLook preview selection of item called");

     return [[displayAC selectedObjects] objectAtIndex:index];
 }

 -(BOOL)previewPanel:(QLPreviewPanel *)panel handleEvent:(NSEvent *)event {

    if (DEBUG) NSLog(@"QuickLook panel error handler called");

// redirect all key down events to the table view

    if ([event type] == NSKeyDown) {
        [myTable keyDown:event];
    return YES;
    }

    return NO;
}

The issue seems to be that the acceptsPreviewPanelControl never gets called, so the delegates never get used (they definitely never get called).

I'm sure this is a simple step that I'm missing, but after dissecting the sample code and scouring over the docs I don't see the answer.

Is it because this is all from within an NSViewController (although I see no reason why that should even come into the equation)?

Any and all help much appreciated.

SOLUTION UPDATE

Thanks to Peter's observation, the fix was a quick one. Don't you hate it when the error message in the debugger means what it says? :-)

In my class that loaded MyViewController I simply needed to add three lines of code to fix the problem.

// mainWindow is an IBOutlet to my window because the calling class 
// is a simple object and not an NSWindowController otherwise I could
// have used `self` instead of `mainWindow`

NSResponder * aNextResponder = [mainWindow nextResponder];

[mainWindow setNextResponder:myViewControllerInstance];
[myViewControllerInstance setNextResponder:aNextResponder];

Job done :-) Thanks Peter.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

╄→承喏 2024-10-02 12:11:51

如果您(还)不是它的代表,为什么您希望它向您发送代表消息?如果您希望它向您发送委托消息,那么您需要将自己设置为其委托。

我尝试调用 setDelegate,但如果我继续沿着这条路线走下去,就会收到关于即将到来的厄运的警告...

<块引用>

[QL] QLError()-[QLPreviewPanel setDelegate:] 在面板没有控制器时调用 - 修复此问题,否则很快就会引发此问题。请参阅 QLPreviewPanel.h 中 -acceptsPreviewPanelControl:/-beginPreviewPanelControl:/-endPreviewPanelControl: 的注释。

“没有控制器”,它说。所以,你需要它有一个控制器。

该标头的注释,尤其是 acceptsPreviewPanelControl: 和 QLPreviewPanel 实例方法 updateController 的注释,表明面板的控制器(当有控制器时)是响应程序中的一个对象链。因此,如果您的控制器没有成为面板的控制器,那是因为您的控制器不在响应者链中。

所以,解决这个问题,然后它就会起作用。

我想只要你的视图控制器的视图或其任何子视图位于响应者链中,你的视图控制器就应该位于响应者链中,但也许情况并非如此。 文档没有说明。如果所有其他方法都失败,请明确将自己设置为某个视图的下一个响应者(并将其上一个下一个响应者设置为您的下一个响应者),然后向预览面板发送一条 updateController 消息。

Why would you expect it to send you delegate messages if you aren't (yet) its delegate? If you want it to send you delegate messages, then you need to set yourself as its delegate.

I tried calling a setDelegate, but get warned about impending doom if I continue down that route...

[QL] QLError(): -[QLPreviewPanel setDelegate:] called while the panel has no controller - Fix this or this will raise soon. See comments in QLPreviewPanel.h for -acceptsPreviewPanelControl:/-beginPreviewPanelControl:/-endPreviewPanelControl:.

“No controller”, it says. So, you need it to have a controller.

The comments on that header, particularly on acceptsPreviewPanelControl: and the QLPreviewPanel instance method updateController, suggest that the panel's controller, when it has one, is an object that is in the responder chain. Therefore, if your controller is not becoming the panel's controller, it's because your controller isn't in the responder chain.

So, fix that, and then it'll work.

I would imagine that your view controller should be in the responder chain whenever its view or any subview thereof is in the responder chain, but maybe this isn't the case. The documentation doesn't say. If all else fails, set yourself as some view's next responder explicitly (and its previous next responder as your next responder), then send the preview panel an updateController message.

羁绊已千年 2024-10-02 12:11:51

这么多年过去了,在swift的世界里,我发现这行代码也能用。
无需重新排列默认响应链,只需“推送”您的视图控制器成为窗口中的第一个响应者。我不确定它是否适用于每种情况:

view.window?.makeFirstResponder(self)

并且对象设置是相同的:

override func acceptsPreviewPanelControl(_ panel: QLPreviewPanel!) -> Bool {
    return true
}

override func beginPreviewPanelControl(_ panel: QLPreviewPanel!) {
    panel.dataSource = self
    panel.delegate = self
    panel.currentPreviewItemIndex = //your initial index
}

override func endPreviewPanelControl(_ panel: QLPreviewPanel!) {
    panel.dataSource = nil
    panel.delegate = nil
}

After so many years, in the swift world, I found this line of code works as well.
Without rearrange the default response chain, just "push" your view controller to be the first responder in the window. I'm not sure if it works for every scenario:

view.window?.makeFirstResponder(self)

And the object setups are the same:

override func acceptsPreviewPanelControl(_ panel: QLPreviewPanel!) -> Bool {
    return true
}

override func beginPreviewPanelControl(_ panel: QLPreviewPanel!) {
    panel.dataSource = self
    panel.delegate = self
    panel.currentPreviewItemIndex = //your initial index
}

override func endPreviewPanelControl(_ panel: QLPreviewPanel!) {
    panel.dataSource = nil
    panel.delegate = nil
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文