【问题标题】:NSOpenPanel (doesn't) validateVisibleColumnsNSOpenPanel(不) validateVisibleColumns
【发布时间】:2013-08-12 17:24:09
【问题描述】:

我有一个NSOpenPanel 和一个accessoryView;在此视图中,用户选择几个单选按钮来更改允许的类型。当面板打开时,正确的文件被启用,另一个被禁用。好的,很好。

现在用户改变了单选按钮,accessoryView 的viewController 观察单选按钮矩阵的变化,从而改变了 NSOpenPanel 的allowedTypes

之后,根据 Apple 文档,它调用 -validateVisibleColumns,但面板中没有任何可见的变化。那就是:正确的文件似乎被禁用了:我可以选择它们,但它们是灰色的! 另一个错误的效果:我选择了一个文件(启用),更改文件类型,(现在错误的)文件保持选中状态,启用了 OK 按钮:但这是错误的文件类型!好像发生了变化但是界面不知道!

我的代码是(选中的是绑定到单选按钮的矩阵):

- (void)observeValueForKeyPath..... 
{
    NSString *extension = (self.selected==0) ? @"txt" : @"xml";
    [thePanel setAllowedFileTypes:@[extension, [extension uppercaseString]]];
    [thePanel validateVisibleColumns];
}

我第一次尝试插入通话

[thePanel displayIfNeeded]

然后我尝试了

[thePanel contentView] setNeedsDisplay]

没有结果。我还尝试实现面板委托方法panel:shouldEnableURL:,应该由validateVisibleColumns调用:我刚刚发现它只被调用了一次,在NSOpenPanel的开头。

有人知道为什么会这样吗?我用沙盒和非沙盒应用程序尝试了所有这些,没有区别。我正在使用 10.8 sdk 进行 ML 开发。

编辑

到目前为止,避免该问题的唯一方法是实现panel:validateURL:error,但这是在用户单击“打开”后调用的,非常糟糕。

【问题讨论】:

  • 你想调用 validateVisibleColumns。我明白了。我没有得到什么。
  • @TBlue:因为当用户在附件视图中做出选择时,我想更改 NSOpenPanel 允许的文件类型。我希望面板禁用某些文件并启用其他一些文件。

标签: objective-c macos nsopenpanel


【解决方案1】:

我有完全相同的问题,低于 10.9,非沙盒,并且在这一天的大部分时间里都在试图找到解决方案!

在对构成 NSOpenPanel 的各种类进行大量修改和深入研究之后(确实是 NSSavePanel),我确实找到了一种方法来强制基础表自行刷新:

id table = [[[[[[[[[[[[_openPanel contentView] subviews][4] subviews][0] subviews][0] subviews][0] subviews][7] subviews][0] subviews][1] subviews][0] subviews][0] subviews][0] subviews][2];
[table reloadData];

当然,编写此 hack 的最佳方法是遍历子视图列表,确保找到正确的类,并最终为后续的 reloadData 调用缓存结束表视图。

我知道,我知道,这是一个非常丑陋的组合,但是,除了“提交错误报告”之外,我似乎找不到任何其他答案来解决这个问题。从我可以看到网上人们自 1.8 以来一直在做的事情! :(

编辑: 这是我现在用来让我的 NSOpenPanel 在 10.9 下正常运行的代码:

- (id) openPanelFindTable: (NSArray*)subviews;
{
    id table = nil;

    for (id view in subviews) {
        if ([[view className] isEqualToString: @"FI_TListView"]) {
            table = view;
            break;
        } else {
            table = [self openPanelFindTable: [view subviews]];
            if (table != nil) break;
        }
    }

    return table;
}


- (void) refreshOpenPanel
{
    if (_openPanelTableHack == nil)
        _openPanelTableHack = [self openPanelFindTable: [[_openPanel contentView] subviews]];
    [_openPanelTableHack reloadData];
    [_openPanel validateVisibleColumns];
}

此代码需要声明两个实例变量_openPanel_openPanelTableHack 才能工作。我将_openPanel 声明为NSOpenPanel*,而_openPanelTableHack 声明为id

现在,不是调用[_openPanel validateVisibleColumns],而是调用[self refreshOpenPanel] 来强制面板按预期更新文件名。我尝试在创建 NSOpenPanel 时缓存表格视图,但是,似乎一旦您“运行”面板,表格视图就会更改,所以我必须在第一次更新时缓存它。

再说一次,这是一个 GIANT hack,但是,我不知道 Apple 需要多长时间才能解决附件视图和文件面板的问题,所以现在,这可行。

如果有人有任何其他不是大杂烩的解决方案,请分享! ;)

【讨论】:

  • 只是一个简短的评论,我注意到原始发布者提到他们在 Mountain Lion 上并使用 10.8 SDK...我实际上在使用 10.8 SDK 时在 10.8.2 下没有任何问题.但是,我在 Mavericks(但针对 10.8 SDK)而不是 Mountain Lion 下编译。
  • 前段时间我解决了...通过避免!即:为不同的文件类型构建不同的菜单。现在我尝试了这个解决方案并且它有效(它是递归编程的一个很好的例子 :-) 无论如何,我们必须监控 Apple 是否可以在未来更改类名(并且可能解决这个错误)
  • 我很高兴我的解决方案对您有用。我确实向 Apple 提交了错误报告,强烈建议您也这样做!希望他们能早日解决这个问题! :)
  • @Eidola 我在 10.10 上尝试过,但无法正常工作。视图层次结构仅包含 contentView 下方的两个视图,它们的 className 均为 NSView。你找到解决办法了吗?
  • 我还没有升级到 10.10,所以我无法访问该系统上的视图层次结构,抱歉!然而,令我惊讶的是,这个错误在 10.10 下仍然存在! :(
【解决方案2】:

Eidola 解决方案的快速实现。 最大的区别是我搜索的是 NSBrowser(子)类而不是特定的类名。在 10.10 上测试(未沙盒)。

private weak var panelBrowser : NSBrowser? //avoid strong reference cycle
func reloadBrowser()
{
    if let assumedBrowser = panelBrowser
    {
        assumedBrowser.reloadColumn(assumedBrowser.lastColumn)
    }
    else if let searchResult = self.locateBrowser(self.panel?.contentView as! NSView)
    {
        searchResult.reloadColumn(searchResult.lastColumn)
        self.panelBrowser = searchResult //hang on to result
    }
    else
    {
        assertionFailure("browser not found")
    }
}

//recursive search function
private func locateBrowser(view: NSView) -> NSBrowser?
{
    for subview in view.subviews as! [NSView]
    {
        if subview is NSBrowser
        {
            return subview as? NSBrowser
        }
        else if let result = locateBrowser(subview)
        {
            return result
        }
    }
    return nil
}

编辑:

好的,所以上面的代码不会一直有效。如果它不起作用并且选择了一个文件(您可以查看详细信息/预览),那么您必须将最后一列重新加载到一列而不是最后一列。重新加载最后两列(确保至少有 2 列)或重新加载所有列。

第二个问题:如果您重新加载列,那么您将失去选择。是的,所选文件/目录仍将突出显示,但面板不会返回正确的 URL。

现在我正在使用这个功能:

func reloadBrowser()
{
    //obtain browser
    if self.panelBrowser == nil
    {
        self.panelBrowser = self.locateBrowser(self.panel?.contentView as! NSView)
    }
    assert(panelBrowser != nil, "browser not found")

    //reload browser
    let panelSelectionPatch = panelBrowser.selectionIndexPaths //otherwise the panel return the wrong urls
    if panelBrowser.lastColumn > 0
    {
        panelBrowser.reloadColumn(panelBrowser.lastColumn-1)
    }
    panelBrowser.reloadColumn(panelBrowser.lastColumn)
    panelBrowser.selectionIndexPaths = panelSelectionPatch
}

【讨论】:

  • 感谢您使用 Swift 更新我的代码,这应该对 10.10 以下的人有所帮助! :)
【解决方案3】:

只需升级到 xcode 6.3(在 Yosemite 10.10.3 上)就可以了。 Apple 修复了这个错误(不再需要 Eidola Hack)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多