【问题标题】:Create NSScrollView Programmatically in an NSView - Cocoa在 NSView 中以编程方式创建 NSScrollView - Cocoa
【发布时间】:2012-04-04 17:42:20
【问题描述】:

我有一个 NSView 类,它负责在 nib 文件中创建的自定义视图。

现在我想将 NSScrollView 添加到自定义视图中,但我需要以编程方式完成,而不是使用 Interface Builder(嵌入到滚动视图中)。

我找到了这段代码:

NSView *windowContentView = [mainWindow contentView];
NSRect windowContentBounds = [windowContentView bounds];
scrollView = [[NSScrollView alloc] init];
[scrollView setBorderType:NSNoBorder];
[scrollView setHasVerticalScroller:YES];
[scrollView setBounds: windowContentBounds];
[windowContentView addSubview:scrollView];

假设我将上面的变量“mainWindow”和“scrollView”声明为 IBOutlets,我将如何将它们连接到 Interface Builder 中的适当组件?这样做有意义吗?

或者有没有更好的方式以编程方式添加滚动视图?

附:我无法以通常的方式连接它们,因为我无法从 Interface Builder 创建一个 NSObject 对象,也无法使用 File Owner..

【问题讨论】:

  • 如果您以编程方式创建视图,它们不会与界面构建器“连接”
  • 什么?当然,您可以将编程视图与界面生成器生成的视图相互关联。

标签: objective-c cocoa nsscrollview


【解决方案1】:

我很难以编程方式使用AutoLayout 创建NSScrollView,但最终让它工作。这是Swift 版本。

// Initial scrollview
let scrollView = NSScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.borderType = .noBorder
scrollView.backgroundColor = NSColor.gray
scrollView.hasVerticalScroller = true

window.contentView?.addSubview(scrollView)
window.contentView?.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[scrollView]|", options: [], metrics: nil, views: ["scrollView": scrollView]))
window.contentView?.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[scrollView]|", options: [], metrics: nil, views: ["scrollView": scrollView]))

// Initial clip view
let clipView = NSClipView()
clipView.translatesAutoresizingMaskIntoConstraints = false
scrollView.contentView = clipView
scrollView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .left, relatedBy: .equal, toItem: scrollView, attribute: .left, multiplier: 1.0, constant: 0))
scrollView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .top, relatedBy: .equal, toItem: scrollView, attribute: .top, multiplier: 1.0, constant: 0))
scrollView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .right, relatedBy: .equal, toItem: scrollView, attribute: .right, multiplier: 1.0, constant: 0))
scrollView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .bottom, relatedBy: .equal, toItem: scrollView, attribute: .bottom, multiplier: 1.0, constant: 0))

// Initial document view
let documentView = NSView()
documentView.translatesAutoresizingMaskIntoConstraints = false
scrollView.documentView = documentView
clipView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .left, relatedBy: .equal, toItem: documentView, attribute: .left, multiplier: 1.0, constant: 0))
clipView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .top, relatedBy: .equal, toItem: documentView, attribute: .top, multiplier: 1.0, constant: 0))
clipView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .right, relatedBy: .equal, toItem: documentView, attribute: .right, multiplier: 1.0, constant: 0))

// Subview1
let view1 = NSView()
view1.translatesAutoresizingMaskIntoConstraints = false
view1.wantsLayer = true
view1.layer?.backgroundColor = NSColor.red.cgColor
documentView.addSubview(view1)
documentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view1]|", options: [], metrics: nil, views: ["view1": view1]))

// Subview2
let view2 = NSView()
view2.translatesAutoresizingMaskIntoConstraints = false
view2.wantsLayer = true
view2.layer?.backgroundColor = NSColor.green.cgColor
documentView.addSubview(view2)
documentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view2]|", options: [], metrics: nil, views: ["view2": view2]))

// Subview3
let view3 = NSView()
view3.translatesAutoresizingMaskIntoConstraints = false
view3.wantsLayer = true
view3.layer?.backgroundColor = NSColor.blue.cgColor
documentView.addSubview(view3)
documentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view3]|", options: [], metrics: nil, views: ["view3": view3]))

// Vertical autolayout
documentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view1(==100)][view2(==200)][view3(==300)]", options: [], metrics: nil, views: ["view1": view1, "view2": view2, "view3": view3]))
documentView.addConstraint(NSLayoutConstraint(item: documentView, attribute: .bottom, relatedBy: .equal, toItem: view3, attribute: .bottom, multiplier: 1.0, constant: 0))

【讨论】:

  • 我找了这么久!非常感谢您提供这个,这正是我正在寻找的,并为我节省了几个小时!
【解决方案2】:

此代码片段应演示如何以编程方式创建 NSScrollView 并使用它来显示任何视图,无论是来自 nib 还是来自代码。在 nib 生成视图的情况下,您只需事先将 nib 文件加载到您的自定义视图中,然后将您的自定义视图的出口 (outletToCustomViewLoadedFromNib) 设置为文件的所有者。

NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:[[mainWindow contentView] frame]];

// configure the scroll view
[scrollView setBorderType:NSNoBorder];
[scrollView setHasVerticalScroller:YES];

// embed your custom view in the scroll view
[scrollView setDocumentView:outletToCustomViewLoadedFromNib];

// set the scroll view as the content view of your window
[mainWindow setContentView:scrollView];

Apple 有一个关于这个主题的指南,我不会链接到它,因为它需要 Apple Developer Connection 访问权限,并且他们的链接经常断开。它的标题是“创建和配置滚动视图”,目前可以通过使用 Google 搜索其标题来找到。

【讨论】:

    【解决方案3】:

    Brian 的回答是正确的,这里是如何在 Swift 4.2 中的 NSScrollView 中创建 NSStackView

    https://github.com/onmyway133/blog/issues/173

    您可能需要翻转NSClipView

    final class FlippedClipView: NSClipView {
      override var isFlipped: Bool {
        return true
      }
    }
    
    
    private func setup() {
      setupScrollView()
      setupStackView()
    }
    
    private func setupScrollView() {
      view.addSubview(scrollView)
      scrollView.translatesAutoresizingMaskIntoConstraints = false
      scrollView.hasVerticalScroller = true
      scrollView.drawsBackground = false
    
      NSLayoutConstraint.activate([
        scrollView.leftAnchor.constraint(equalTo: view.leftAnchor),
        scrollView.rightAnchor.constraint(equalTo: view.rightAnchor),
        scrollView.topAnchor.constraint(equalTo: view.topAnchor),
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -30),
    
        scrollView.heightAnchor.constraint(equalToConstant: 400)
      ])
    
      let clipView = FlippedClipView()
      clipView.drawsBackground = false
      scrollView.contentView = clipView
      clipView.translatesAutoresizingMaskIntoConstraints = false
      NSLayoutConstraint.activate([
        clipView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
        clipView.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
        clipView.topAnchor.constraint(equalTo: scrollView.topAnchor),
        clipView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
      ]
      scrollView.documentView = stackView
    
      stackView.translatesAutoresizingMaskIntoConstraints = false
      NSLayoutConstraint.activate([
        stackView.leftAnchor.constraint(equalTo: clipView.leftAnchor),
        stackView.topAnchor.constraint(equalTo: clipView.topAnchor),
        stackView.rightAnchor.constraint(equalTo: clipView.rightAnchor),
        // NOTE: No need for bottomAnchor
      ])
    }
    
    private func setupStackView() {
      stackView.orientation = .vertical
      stackView.edgeInsets = NSEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
    
      NSLayoutConstraint.activate([
        myRowView.heightAnchor.constraint(equalToConstant: 40)
      ])
    
      myRowView.onPress = { [weak self] in
        self?.doSomething()
      }
    
      stackView.addArrangedSubview(myRowView)
    }
    

    【讨论】:

      【解决方案4】:

      这是一个将NSStackView 以编程方式添加到NSScrollView 的示例。可以使用以下解决方案添加任何视图,但我以 NSStackView 为例

      我使用SnapKit 来保持自动布局代码简洁,但是,您可以使用锚来添加约束,而无需任何第三方依赖

      // Create NSScrollView and add it as a subview in the desired location
      let scrollView = NSScrollView()
      scrollView.borderType = .noBorder
      scrollView.verticalScrollElasticity = .none
      addSubview(scrollView)
      scrollView.snp.makeConstraints { $0.edges.equalToSuperview() } //match edges to superview
      
      // Assign an instance of NSClipView to `contentView` property of `NSScrollView`
      let clipView = NSClipView()
      scrollView.contentView = clipView
      clipView.snp.makeConstraints { $0.edges.equalTo(scrollView) }
      
      // Assign whatever view you want to put inside scroll view to the `documentView` property.
      // Also note I have added just 3 constraints; top bottom and left. That way stackview can freely expand on the right
      scrollView.documentView = stackView
      stackView.snp.makeConstraints { $0.top.bottom.left.equalTo(clipView) }
      
      

      请注意,在添加子视图后我没有使用translatesAutoresizingMaskIntoConstraints,因为 SnapKit 在内部处理它,但如果您使用锚添加约束,请确保所有以编程方式添加的子视图都将 translatesAutoresizingMaskIntoConstraints 设置为 false。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-06
        • 2010-09-25
        • 1970-01-01
        • 1970-01-01
        • 2014-09-08
        • 2010-11-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多