【问题标题】:Using contentLayoutGuide for constraints使用 contentLayoutGuide 进行约束
【发布时间】:2015-04-12 13:03:43
【问题描述】:

在观看了来自 WWDC 的关于新 UI 高级功能的会议后,我正在尝试在 OSX 10.10 中使用带有新 Vibrant 主题的 NSTextView。我的文本视图没有放置在标题栏附近,因此我已将内容视图设置为完全大小,以便我可以将文本视图放置在标题栏下方。

我的问题是我在文本视图和标题栏之间有一个NSView,它应该放在标题栏的正下方,但我无法获得自动布局来为我执行此操作。在视频中,他们使用NSWindow 中的新contentLayoutGuide 属性提供了此示例代码:

NSLayoutConstraint *topEdgeConstraint = [NSLayoutConstraint constraintWithItem:myView attribute: NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:window.contentLayoutGuide attribute:NSLayoutAttributeTop];

但是,每当我尝试安装 Xcode 给我的约束时,我都无法让它工作

无法在视图上安装约束。约束是否引用了视图子树之外的内容?这是违法的。

我知道示例中使用的窗口属性不在视图的子树中,因为视图位于窗口内,但是我应该如何访问 contentLayoutGuide 属性?

目前我正在尝试在子类NSWindow-awakeFromNib 方法中添加约束。可能是错误的方法?

提前致谢

【问题讨论】:

    标签: objective-c xcode macos cocoa osx-yosemite


    【解决方案1】:

    我最近遇到了这个问题,并认为我今天会分享推荐的处理方法。该技术可以在 WWDC 2016 Session 239“Crafting Modern Cocoa Apps”中看到。自从 API 发布以来,API 已经发生了变化,所以有一个转折。

    以下代码通过使用 windows contentLayoutGuide 将其限制在内容视图的顶部,将一个名为 searchField 的 NSSearchField 放在标题/工具栏区域的正下方。下面的代码被添加到一个 NSViewController 中。

    @IBOutlet weak var searchField: NSSearchField!
    private var titleBarConstraint: NSLayoutConstraint?
    
    override func updateViewConstraints() {
       if titleBarConstraint == nil {
          if let topAnchor = (searchField.window?.contentLayoutGuide as? NSLayoutGuide)?.topAnchor {
             titleBarConstraint = searchField.topAnchor.constraint(equalTo: topAnchor, constant: 8)
             titleBarConstraint?.isActive = true
          }
       }
    
       super.updateViewConstraints()
    }
    

    在 Interface Builder 中,NSSearchField 使用自动布局定位,以匹配运行时的外观。单击 NSSearchField 的顶部约束并启用“Placeholder Remove at build time”复选框。

    contentLayoutGuide 仍然返回 Any? 但它是一个 NSLayoutGuide。这就是转折。

    【讨论】:

    • 不错的答案,但我有一个问题。我在 NSTabView 中使用它。当切换到另一个选项卡并返回此选项卡时,不再应用约束。然后 titleBarConstraint 不是 nil 并且不会被重新创建。我可以删除 nil 测试,但这意味着每次显示视图时都创建一个约束,这似乎有点矫枉过正。我该怎么办?
    【解决方案2】:

    我已经记录了我在此处获取带有 textview 的滚动视图以在窗口标题栏下滚动的整个过程(包括 Apple 将事物限制在顶部的方法):https://stackoverflow.com/a/36435872/4615448

    使用插入和跟踪插入符号/光标的相同技术也适用于您。您仍然可以让您的滚动视图顶部指南转到 contentView 的顶部,然后使用插图使其出现在内容视图中并位于标题栏下方。你也可以在它上面覆盖其他视图,只要它在它们下面延伸。然后,您可以适当地更新插图,它会落后于您需要的任何内容。

    (我知道,这个问题已经很老了——你可能已经想通了。我来到这里时试图在 El Capitan 10.11 中解决同样的问题,所以我把这个留给其他人) .

    我对 OS X 开发比较陌生,所以欢迎各位专业人士对这篇文章或下一篇文章发表评论,并指出任何改进或见解。

    【讨论】:

      【解决方案3】:

      编辑:此解决方案不再可接受,因为 OS X El Capitan 在窗口框架中具有不同的视图层次结构。查找sam's answer,他建议在其中使用 Yosemite 中引入的新 NSLayoutConstraint 方法和属性(约束激活)。

      我昨天也遇到了同样的问题?。

      不幸的是,文档缺少有关放置此约束的位置的信息。新的 Xcode 'Capture View Hierarchy' 功能助你一臂之力!

      当您约束两个视图的某些属性时,您必须将生成的约束放在这些视图的第一个共同祖先上。这是基本的自动布局规则。

      在您的代码中,您引用了 window.contentLayoutGuide,它声明为 id,但实际上是 NSContentLayoutView。运行您的应用程序并捕获视图层次结构。以下是您在优胜美地应该得到的:

      您必须将约束添加到NSThemeFrame 视图,因为这是子树包含myViewNSContentLayoutView 的第一个祖先。这个私有视图由 NSWindow 管理,没有明显的属性可以从窗口实例中获取它,但是我们可以轻松地遍历视图层次结构。

      这是一个完整的代码示例:

      NSWindow* window = <Get the window>;
      
      NSLayoutConstraint* constraint = [NSLayoutConstraint constraintWithItem: myView attribute: NSLayoutAttributeTop relatedBy: NSLayoutRelationEqual toItem: window.contentLayoutGuide attribute: NSLayoutAttributeTop];
      
      [((NSView*)window.contentLayoutGuide).superview addConstraint: constraint];
      

      ?

      【讨论】:

      • 正如 Sam 下面提到的,使用 active 属性是要走的路。假设contentLayoutGuideNSView 的一个实例非常不好。
      【解决方案4】:

      在 Yosemite 中,约束现在具有“活动”属性,您可以设置该属性以使约束处于活动状态,而无需将其放置在共同的祖先上。我认为它是专门为这种你不知道共同祖先的情况而创建的。

      NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem: myView attribute: NSLayoutAttributeTop relatedBy: NSLayoutRelationEqual toItem: window.contentLayoutGuide attribute: NSLayoutAttributeTop];
      constraint.active = YES
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-13
        • 2016-10-11
        • 2021-06-22
        • 1970-01-01
        相关资源
        最近更新 更多