以编程方式将NSTableView添加到NSStackView

时间:2017-09-12 03:06:14

标签: nstableview macos-sierra nsstackview

我想知道使用Swift 3 / MacOS Sierra以编程方式在NSStackView中添加NSTableView。

这个想法是说2个NSTextFields通过.leading重力空间中的centerY轴对齐,然后是.center重力空间中的tableview,然后通过.trailing重力空间中的中心Y轴对齐另外2个NSTextFields。堆栈视图将跨越NSView的宽度 - 就像标题一样。

这是一个好主意还是应该避免这样做?要让它看起来正确是非常困难的 - 尽管添加了约束以试图将其固定到固定宽度,但表格总是有太大的宽度。

任何见解都将受到赞赏。我是编程MacOS的新手。

谢谢,

以下是Interface Builder中的输出: output of the headerview

这是我正在使用的NSView的代码: 视图控制器在其他地方,但我真的没有视图控制器的问题 - 它正确地显示表中的数据。这只是tableview的大小/定位(我试图通过NSStackView在NSView中做)总是错误的。它应该有650的宽度,但宽度为907,我在调试控制台中始终得到相同的错误:

  

2017-09-12 17:43:36.041062-0500 RaceProgram [795:36958] [布局]检测到< RacingProgram.RaceImportViewHeader:0x6000001ccd50>。它无法放置,因为没有足够的约束来完全定义大小和原点。添加缺少的约束,或设置translatesAutoresizingMaskIntoConstraints = YES,并为您生成约束。如果此视图在macOS 10.12及更高版本上手动布局,您可以选择不从覆盖中调用[super layout]。在DETECTED_MISSING_CONSTRAINTS上设置断点以进行调试。此错误仅记录一次。

import Cocoa

@IBDesignable
class RaceImportViewHeader: NSView {

// MARK: Properties
private var raceQualificationsTableView:NSTableView

private var raceImportHeaderStackView:NSStackView

private var raceNumberTitle: NSTextField
private var raceNumberValue: NSTextField

public var raceQualificationsTableRowHeight: CGFloat            

@IBInspectable var borderColor:NSColor = .black
@IBInspectable var backgroundColor:NSColor = .lightGray

enum InitMethod {
    case Coder(NSCoder)
    case Frame(CGRect)
}

override convenience init(frame: CGRect) {
    self.init(.Frame(frame))!
}

required convenience init?(coder: NSCoder) {
    self.init(.Coder(coder))
}

private init?(_ initMethod: InitMethod) {
    // Group together the initializers for this view class

    raceQualificationsTableView = NSTableView()

    raceImportHeaderStackView = NSStackView()

    raceNumberTitle = NSTextField()
    raceNumberValue = NSTextField()

    raceQualificationsTableRowHeight = 17.0         // Initialize the row height for raceQualifications

    switch initMethod {
    case let .Coder(coder): super.init(coder: coder)
    case let .Frame(frame): super.init(frame: frame)
    }

    self.translatesAutoresizingMaskIntoConstraints = false

    drawUI()
}


override func draw(_ dirtyRect: NSRect) {
    super.draw(dirtyRect)

    let viewSize: NSRect = self.frame
    let newRect = NSRect(x: 0, y: 0, width: viewSize.width, height: viewSize.height)


    // Outline the Header --> Only for layout debug purposes
    let path = NSBezierPath(rect: newRect)
    backgroundColor.setFill()
    path.fill()
    borderColor.setStroke()         // Set the stroke color
    path.stroke()                   // Fill the stroke or border of the rectangle

}

// MARK: UI Construction

func drawUI() {
    let viewFrame = self.frame       // with respect to the super class
    let viewBounds = self.bounds     // with respect to the view

    // MARK: Race Number Setup

    func addRaceNumberTitle(startingPositionX: CGFloat) {
        // This configures label for race Number

        let width:CGFloat = 60.0                    //Arbitrary at the moment
        let height:CGFloat = 40.0
        let leftPadding:CGFloat = 2.5              // The super view (frame)is the NSView in this case
        let topPadding:CGFloat = (viewBounds.height - height)/2
        let raceNumberTitleNSRect = NSRect(x: leftPadding + startingPositionX, y: viewBounds.height - height - topPadding, width: width, height: height)

        //Swift.print("The raceNumberTitleNSRect title NSRect is \(raceNumberTitleNSRect)")

        raceNumberTitle = NSTextField(frame: raceNumberTitleNSRect)
        raceNumberTitle.stringValue = "Race\nNumber"
        raceNumberTitle.maximumNumberOfLines = 2
        raceNumberTitle.isEditable = false
        raceNumberTitle.isBordered = false
        raceNumberTitle.alignment = .center
        raceNumberTitle.backgroundColor = .clear
        raceNumberTitle.sizeToFit()

        let updatedHeight = raceNumberTitle.frame.height
        let newUpdatedPadding = (viewBounds.height - updatedHeight) / 2
        let oldOriginX = raceNumberTitle.frame.origin.x
        let newOriginY = viewBounds.height - updatedHeight - newUpdatedPadding

        let newOrigin = NSPoint(x: oldOriginX, y: newOriginY)
        raceNumberTitle.setFrameOrigin(newOrigin)

        //addSubview(raceNumberTitle)      // Add to view
        raceImportHeaderStackView.addView(raceNumberTitle, in: .leading)
    }

    func addRaceNumberValue(startingPositionX: CGFloat) {
        // This configures value label for race number

        let width:CGFloat = 20.0                    //Arbitrary at the moment
        let height:CGFloat = 40.0
        let leftPadding:CGFloat = 5.0               // The super view (frame)is the NSView in this case
        let topPadding:CGFloat = (viewBounds.height - height)/2
        let raceNumberInRect = NSRect(x: startingPositionX + leftPadding, y: viewBounds.height - height - topPadding, width: width, height: height)

        Swift.print("The raceNumberInRect title NSRect is \(raceNumberInRect)")

        raceNumberValue = NSTextField(frame: raceNumberInRect)
        raceNumberValue.identifier = "raceNumber"
        raceNumberValue.placeholderString = "1"
        raceNumberValue.font = NSFont(name: "Impact", size: 20.0)
        raceNumberValue.maximumNumberOfLines = 1
        raceNumberValue.isEditable = false
        raceNumberValue.isBordered = true
        raceNumberValue.alignment = .center
        raceNumberValue.backgroundColor = .clear
        raceNumberValue.sizeToFit()


        let updatedHeight = raceNumberValue.frame.height
        let oldOriginX = raceNumberValue.frame.origin.x
        let newUpdatedPadding = (viewBounds.height - updatedHeight) / 2
        let newOriginY = viewBounds.height - updatedHeight - newUpdatedPadding

        let newOrigin = NSPoint(x: oldOriginX, y: newOriginY)
        raceNumberValue.setFrameOrigin(newOrigin)

        //addSubview(raceNumberValue)      // Add to view
        raceImportHeaderStackView.addView(raceNumberValue, in: .leading)
    }


    // MARK: Race Qualifications Table Setup

    func addRaceQualificationsTable(startingPositionX: CGFloat) {

        // Padding variables
        let leftPadding:CGFloat = 5.0
        let topPadding:CGFloat = 5.0

        // Table Properties
        let width:CGFloat = 650.0
        let height:CGFloat = 40

        let tableRect = CGRect(x: startingPositionX + leftPadding, y: viewBounds.height - height - topPadding, width: width, height: height)

        //let insetForTableView:CGFloat = 1.0
        //let scrollRect = CGRect(x: tableRect.origin.x-insetForTableView, y: tableRect.origin.y-insetForTableView, width: tableRect.width+2*insetForTableView, height: tableRect.height+2*insetForTableView)

        let tableNSSize = NSSize(width: tableRect.width, height: tableRect.height)
        let scrollNSRect = NSScrollView.frameSize(forContentSize: tableNSSize, horizontalScrollerClass: nil, verticalScrollerClass: nil, borderType: .bezelBorder, controlSize: .regular, scrollerStyle: .legacy)

        Swift.print("tableRect \(tableRect)")
        Swift.print("scrollNSRect \(scrollNSRect)")
        //Swift.print("scrollRect \(scrollRect)")

        let scrollViewOrigin:CGPoint = tableRect.origin
        let scrollViewNSSize:CGSize = scrollNSRect
        let scrollRect = NSRect(origin: scrollViewOrigin, size: scrollViewNSSize)

        Swift.print("scrollRect \(scrollRect)")

        let tableScrollView = NSScrollView(frame: scrollRect)
        raceQualificationsTableView = NSTableView(frame: tableRect)
        raceQualificationsTableView.identifier = "raceQualificationsTable"          // Setup identifier
        raceQualificationsTableView.rowHeight = 20.0

        Swift.print("instrinic size \(raceQualificationsTableView.intrinsicContentSize)")

        //Swift.print("tableScrollView contentsize \(tableScrollView.contentSize)")

        tableScrollView.documentView = raceQualificationsTableView
        tableScrollView.autoresizingMask = .viewNotSizable
        Swift.print("tableScroll content size \(tableScrollView.contentSize)")
        //self.addSubview(tableScrollView)

        raceImportHeaderStackView.addView(tableScrollView, in: .center)


    }

    func configureRaceQualificationsTable(showRaceNumberCol: Bool, showRaceCodeCol: Bool) {

        let headerAlignment = NSTextAlignment.center    // Easy way to change justification of headers

        // MARK: Race Number Column Options
        let raceNumberColumn = NSTableColumn(identifier: "raceNumberCol")
        raceNumberColumn.title = "Race"
        raceNumberColumn.minWidth = 40.0
        raceNumberColumn.width = 40.0
        raceNumberColumn.headerToolTip = "Race Number from the Imported Card"
        raceNumberColumn.headerCell.alignment = headerAlignment
        // Note: Word Race is always going to be wider than the race number value
        // So size to Fit is appropriate here.
        raceNumberColumn.sizeToFit()

        if showRaceNumberCol {
            // Option of not adding this to the table
            raceQualificationsTableView.addTableColumn(raceNumberColumn)
        }


        // MARK: Driver Column Options
        let breedColumn = NSTableColumn(identifier: "driverCol")
        driverColumn.title = "Driver"
        driverColumn.minWidth = 10
        driverColumn.headerToolTip = "Driver information"
        driverColumn.headerCell.alignment = headerAlignment
        driverColumn.sizeToFit()
        raceQualificationsTableView.addTableColumn(driverColumn)

        // MARK: Race Code Column Options
        let raceTypeCodeColumn = NSTableColumn(identifier: "raceTypeCodeCol")
        raceTypeCodeColumn.title = "Race Code"
        raceTypeCodeColumn.minWidth = 40
        raceTypeCodeColumn.headerToolTip = "Race Classification Code"
        raceTypeCodeColumn.headerCell.alignment = headerAlignment
        raceTypeCodeColumn.sizeToFit()

        if showRaceCodeCol {
            // Option of not adding to the table
            raceQualificationsTableView.addTableColumn(raceTypeCodeColumn)
        }

        // MARK: Race Type Code Description Options
        let raceTypeCodeDescColumn = NSTableColumn(identifier: "raceTypeCodeDescCol")
        raceTypeCodeDescColumn.title = "Race Desc"
        raceTypeCodeDescColumn.minWidth = 50
        raceTypeCodeDescColumn.width = 100
        raceTypeCodeDescColumn.headerToolTip = "Race Classification Full Description"
        raceTypeCodeDescColumn.headerCell.alignment = headerAlignment

        raceQualificationsTableView.addTableColumn(raceTypeCodeDescColumn)

        // MARK: Race Restriction Column Options
        let raceRestrictionColumn = NSTableColumn(identifier: "raceRestrictionCol")
        raceRestrictionColumn.title = "Restrictions"
        raceRestrictionColumn.minWidth = 50
        raceRestrictionColumn.width = 80
        raceRestrictionColumn.headerToolTip = "Race Restrictions"
        raceRestrictionColumn.headerCell.alignment = headerAlignment

        raceQualificationsTableView.addTableColumn(raceRestrictionColumn)

        // MARK: Sex Restriction Column Options
        let sexRestrictionColumn = NSTableColumn(identifier: "sexRestrictionCol")
        sexRestrictionColumn.title = "Sex"
        sexRestrictionColumn.minWidth = 100
        sexRestrictionColumn.width = 100
        sexRestrictionColumn.headerToolTip = "Sex Restrictions"
        sexRestrictionColumn.headerCell.alignment = headerAlignment

        raceQualificationsTableView.addTableColumn(sexRestrictionColumn)

        // MARK: Age Restriction Column Options
        let ageRestrictionColumn = NSTableColumn(identifier: "ageRestrictionCol")
        ageRestrictionColumn.title = "Age"
        ageRestrictionColumn.minWidth = 100
        ageRestrictionColumn.width = 100
        ageRestrictionColumn.headerToolTip = "Age Restrictions"
        ageRestrictionColumn.headerCell.alignment = headerAlignment

        raceQualificationsTableView.addTableColumn(ageRestrictionColumn)

        // MARK: Division Column Options
        let divisionColumn = NSTableColumn(identifier: "divisionCol")
        divisionColumn.title = "Division"
        divisionColumn.minWidth = 50

        let minDivisionColumnWidth = raceQualificationsTableView.frame.width - raceNumberColumn.width - driverColumn.width - raceTypeCodeColumn.width - raceTypeCodeDescColumn.width - raceRestrictionColumn.width - sexRestrictionColumn.width - ageRestrictionColumn.width

        // Calculate the available room for the division column
        if (showRaceCodeCol && showRaceNumberCol) {
            // This is the minimum case
            // No idea why we need the 25.0 manual adjustment
            divisionColumn.width = minDivisionColumnWidth - 25.0
        } else if (showRaceCodeCol && !showRaceNumberCol) {
            // Add back race type code
            // No idea why we need to manually adjust 53.5
            divisionColumn.width = minDivisionColumnWidth + raceTypeCodeColumn.width - 53.5
        } else if (!showRaceCodeCol && showRaceNumberCol) {
            // Add back race number col
            divisionColumn.width = minDivisionColumnWidth + raceNumberColumn.width
        } else {
            // Else it's the maximum space
            // This code was making the frame too large -- it was increasing the
            // the frame size of the column to 670.0  I put a manual reduction of
            // 20 to keep the frame size the same.  Not sure where this 20 is coming from.
            divisionColumn.width = minDivisionColumnWidth + raceNumberColumn.width + raceTypeCodeColumn.width - 20.0
        }

        //Swift.print("The division column width is \(divisionColumn.width)")

        divisionColumn.headerToolTip = "Division -- Unknown what this means"
        divisionColumn.headerCell.alignment = headerAlignment

        raceQualificationsTableView.addTableColumn(divisionColumn)

        //Swift.print("raceQualificationsTableView.frame.width is \( raceQualificationsTableView.frame.width)")
    }

    // MARK: Race Distance Surface Course Setup

    func addRaceDistanceSurfaceCourseTable(startingPositionX: CGFloat) {
        // Table Properties
        let width:CGFloat = 250.0
        let height:CGFloat = 40.0

        // Padding variables
        let leftPadding:CGFloat = 5.0

        let topPosition:CGFloat = (viewBounds.height - ((viewBounds.height - height)/2) - height)

        let tableRect = CGRect(x: leftPadding + startingPositionX, y: topPosition, width: width, height: height)

        let tableScrollView = NSScrollView(frame: tableRect)
        raceDistanceSurfaceCourseTableView = NSTableView(frame: tableRect)
        raceDistanceSurfaceCourseTableView.identifier = "raceDistanceSurfaceCourseTable"          // Setup identifier
        //raceDistanceSurfaceCourseTableView.rowHeight = 20.0
        raceDistanceSurfaceCourseTableView.intercellSpacing = NSSize(width: 1.0, height: 1.0)
        raceDistanceSurfaceCourseTableView.headerView = ImportRaceTableHeaders()

        tableScrollView.documentView = raceDistanceSurfaceCourseTableView
        //tableScrollView.hasVerticalScroller = false
        //tableScrollView.verticalScroller = nil          // Turn off vertical scrolling
        //tableScrollView.verticalScrollElasticity = .none

        //raceDistanceSurfaceCourseTableView = NSTableViewHeader

        //self.addSubview(tableScrollView)
        raceImportHeaderStackView.addView(raceDistanceSurfaceCourseTableView, in: .center)

    }



    // MARK: Construct the fields:

    //configureHeaderView()
    configureStackView()
    addRaceNumberTitle(startingPositionX: 0.0)              // Add the race number title
    addRaceNumberValue(startingPositionX: raceNumberTitle.frame.origin.x + raceNumberTitle.frame.width)   //Add the Race Number value text field

    addRaceQualificationsTable(startingPositionX: raceNumberValue.frame.origin.x + raceNumberValue.frame.width)
    configureRaceQualificationsTable(showRaceNumberCol: false, showRaceCodeCol: false)

}

// MARK: TableView Functions

func reloadTableViewData(identifier: String) {

    Swift.print("Manual reload of data for identifier \(identifier)")

    switch identifier {

        case "raceQualificationsTable":
            raceQualificationsTableView.reloadData()
        case "raceDistanceSurfaceCourseTable":
            raceDistanceSurfaceCourseTableView.reloadData()
        default:
            break
    }
}


// MARK: Delegate/DataSources Outlets for TableViews

// Race Qualification Table (the header table)
@IBOutlet weak var raceQualificationsDelegate: NSTableViewDelegate? {
    get {
        return raceQualificationsTableView.delegate
    }
    set {
        raceQualificationsTableView.delegate = newValue
    }
}

@IBOutlet weak var raceQualificationsDataSource: NSTableViewDataSource? {
    get {
        return raceQualificationsTableView.dataSource
    }
    set {
        raceQualificationsTableView.dataSource = newValue
    }
}

// Race Distance Surface Course
@IBOutlet weak var raceDistanceSurfaceCourseDelegate: NSTableViewDelegate? {
    get {
        return raceDistanceSurfaceCourseTableView.delegate
    }
    set {
        raceDistanceSurfaceCourseTableView.delegate = newValue
    }
}

@IBOutlet weak var raceDistanceSurfaceCourseDataSource: NSTableViewDataSource? {
    get {
        return raceDistanceSurfaceCourseTableView.dataSource
    }
    set {
        raceDistanceSurfaceCourseTableView.dataSource = newValue
    }
}

// MARK: Label Outlets
@IBOutlet var raceNumber:String? {
    get {
        return raceNumberValue.stringValue
    }
    set {
        raceNumberValue.stringValue = newValue!
    }
}

}

0 个答案:

没有答案