Resize NSScrollView within superview

112 Views Asked by At

I’m embarrassed to be asking this question, but even with the wealth of information available on SO and Internet searches, I’m unable to to accomplish my goal, which is to resize an NSScrollView contained within an NSView.

The details: I have an NSViewController that is the window content of the main application window. The view controller contains an NSView to which I’ve programmatically added an NSScrollView, which in itself contains an NSTableView. The main application window and NSViewController are the freebies I get with IB, scroll view and table view are created programatically.

The NSTableView displays the rows and single column I’ve created as expected, but when I resize the window in the horizontal and vertical dimensions, the scroll view doesn’t resize. It appears that the containing view is restricted by the size I specify in creating the scroll view, but without a size the scroll view doesn’t call its delegate methods. My attempts to address that behavior don’t result in expected behavior and so clearly I don’t fully understand the cause of the problem.

My question then is this: what do I need to do to have the scroll view match the containing view when I resize the window?

//
//  MyViewController.swift
//  HelloTableViewXX
//
//

import Cocoa

fileprivate let ME = "ViewController"

class ViewController: NSViewController
{
    private var dataArray: [String] = []
    
    override func viewDidAppear()
    {
        super.viewDidAppear()
        
        setupView()
        setupTableView()
    }
    
    func setupView ()
    {
        self.view.translatesAutoresizingMaskIntoConstraints = false
    }
    
    func setupTableView ()
    {
        let tableView = NSTableView ()
        
        tableView.headerView = nil
        
        let column = NSTableColumn ()
        column.identifier = NSUserInterfaceItemIdentifier(rawValue: "TableColumn")
        column.width = 400
        column.minWidth = 40
        column.maxWidth = 4000
        
        tableView.addTableColumn(column)
       
        tableView.delegate = self
        tableView.dataSource = self
        
        let scrollView = NSScrollView (frame: self.view.bounds)
        
        //scrollView.hasHorizontalScroller = true
        scrollView.hasVerticalScroller = true
        
        self.view.addSubview(scrollView)
        
        scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
        scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
        scrollView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
        
        scrollView.documentView = tableView
    }
}

extension ViewController : NSTableViewDataSource
{
    func numberOfRows(in tableView: NSTableView) -> Int
    {        
        return 20        
    }
}

extension ViewController : NSTableViewDelegate
{
    func tableView (_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
    {
        let text = "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz"
        var v = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier.init(rawValue: "TableColumn"), owner: self) as? NSTextField
        if v == nil
        {
            v = NSTextField ()
            v?.identifier = NSUserInterfaceItemIdentifier(rawValue: "TableColumn")
            v?.maximumNumberOfLines = 1
        }
        else
        {
            print (ME + ".\(#function) tableView reuse")
        }
        
        v!.stringValue = text
        v!.font = NSFont.monospacedSystemFont(ofSize: 10, weight: .regular)
        
        return v!
    }    
}
1

There are 1 best solutions below

0
Hayseed On

I have an answer for this specific issue. I created a separate project, with IB instantiated NSViewController, and an embedded NSScrollView and NSTableView, giving the expected view hierarchy of controller, view, scrollview, clip view, etc., and configured the settings in IB to produce the results I wanted. I then opened the storyboard in an XML editor, and used the definitions as a guide for the settings in the project with my programmatically set scroll view and table view, which solved my problem. I now have a resizing scroll view and table as the window is resized. The code looks like this:

import Cocoa

class MyTableViewController: NSViewController
{
    private var initialized = false

    private var dataArray: [String] = []
    
    override func viewDidAppear()
    {
        super.viewDidAppear()
        
        loadData()
        //setupView()
        setupTableView()
    }
    
    func setupView ()
    {
    }
    
    func setupTableView ()
    {
        let tableView = NSTableView ()
        
        tableView.headerView = nil
        tableView.columnAutoresizingStyle = .lastColumnOnlyAutoresizingStyle
        tableView.autoresizesSubviews = true
        tableView.autoresizingMask = [.width, .height]
        
        let column = NSTableColumn ()
        column.identifier = NSUserInterfaceItemIdentifier(rawValue: "TableColumn")
        column.width = 426
        column.minWidth = 40
        column.maxWidth = 1000
        column.resizingMask = [.autoresizingMask, .userResizingMask] // verify in debugger
        
        tableView.addTableColumn(column)
    
        tableView.delegate = self
        tableView.dataSource = self
        
        let scrollView = NSScrollView (frame: self.view.bounds)
        scrollView.autoresizesSubviews = true
        scrollView.autoresizingMask = [.height, .width]
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        
        scrollView.hasHorizontalScroller = true
        scrollView.hasVerticalScroller = true
        
        self.view.addSubview(scrollView)
        
        self.view.addConstraint(NSLayoutConstraint (item: self.view, attribute: .trailing, relatedBy: .equal, toItem: scrollView, attribute: .trailing, multiplier: 1.0, constant: 20))
        self.view.addConstraint(NSLayoutConstraint (item: scrollView, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1.0, constant: 20))
        self.view.addConstraint(NSLayoutConstraint (item: self.view, attribute: .bottom, relatedBy: .equal, toItem: scrollView, attribute: .bottom, multiplier: 1.0, constant: 20))
        self.view.addConstraint(NSLayoutConstraint (item: scrollView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1.0, constant: 20))
        
        scrollView.documentView = tableView
    }
}
   
extension MyTableViewController : NSTableViewDataSource
{
    func numberOfRows(in tableView: NSTableView) -> Int
    {        
        return dataArray.count        
    }
}

extension MyTableViewController : NSTableViewDelegate
{
    func tableView (_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
    {
        let text = dataArray [row]
        var v = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier.init(rawValue: "TableColumn"), owner: self) as? NSTextField
        if v == nil
        {
            v = NSTextField ()
            v?.identifier = NSUserInterfaceItemIdentifier(rawValue: "TableColumn")
            v?.maximumNumberOfLines = 1
            v?.autoresizingMask = [.width]
            v?.setContentHuggingPriority(NSLayoutConstraint.Priority(rawValue: 251), for: .horizontal)
            v?.translatesAutoresizingMaskIntoConstraints = false
        }
        else
        {
            print (ME + ".\(#function) tableView reuse")
        }
        
        v!.stringValue = text
        v!.font = NSFont.monospacedSystemFont(ofSize: 10, weight: .regular)
        v!.frame = CGRect(x: 0, y: 0, width: 3000, height: 0)

        return v!
    }