Border Doesn't Draw in NSView When inside NSTableCellView

145 views Asked by At

I have three NSViews inside an NSTableCellView. Depending on the data for the row, I want to show a selected role with a border on the NSView like this:

enter image description here

The blue border NSView is a subclass that looks like this:

class RolePill: NSView{
  override func draw(_ dirtyRect: NSRect)  {
    super.draw(dirtyRect)
    layer?.cornerRadius = 9
    layer?.borderWidth = 2
    layer?.borderColor = NSColor.clear.cgColor
  }
}

The role gets set initially when my table loads like this:

extension UsersVC: NSTableViewDelegate, NSTableViewDataSource{
  func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "UserCell"), owner: nil) as! UserCell

    let user = users[row]

    //Role
    cell.setRole(role: user.role)
  }
}

And my table cell, where the role gets set on load and on a button click, is set up like this:

class UserCell: NSTableCellView{
  @IBOutlet weak var wrapOwner: RolePill!
  @IBOutlet weak var wrapAdmin: RolePill!
  @IBOutlet weak var wrapUser: RolePill!

  @IBAction func clickOwner(_ sender: NSButton) {
    setRole(role: "owner")
  }
  @IBAction func clickAdmin(_ sender: NSButton) {
    setRole(role: "admin")
  }
  @IBAction func clickUser(_ sender: NSButton) {
    setRole(role: "user")
  }
  
  func setRole(role: String){
    let selectedColor = getAccentColor()
    let offColor = Color(named: "BravoDark")!
    let offTextColor = Color(named: "BFC0C2")
    
    switch role{
      case "owner":
        wrapOwner.layer?.borderColor = selectedColor.cgColor
        wrapAdmin.layer?.borderColor = offColor.cgColor
        wrapUser.layer?.borderColor = offColor.cgColor
      
        labelOwner.textColor = Color.white
        labelAdmin.textColor = offTextColor
        labelUser.textColor = offTextColor
      case "admin":
        wrapOwner.layer?.borderColor = offColor.cgColor
        wrapAdmin.layer?.borderColor = selectedColor.cgColor
        wrapUser.layer?.borderColor = offColor.cgColor
        
        labelOwner.textColor = offTextColor
        labelAdmin.textColor = Color.white
        labelUser.textColor = offTextColor
        
      default:
        wrapOwner.layer?.borderColor = offColor.cgColor
        wrapAdmin.layer?.borderColor = offColor.cgColor
        wrapUser.layer?.borderColor = selectedColor.cgColor
      
        labelOwner.textColor = offTextColor
        labelAdmin.textColor = offTextColor
        labelUser.textColor = Color.white 
    }
    
  }
}

When the table loads initially, and anytime it refreshes (tableView.reloadData()) I lose my border and it looks like this:

enter image description here

As you can see, the textColor is set correctly to white. But for some reason, the border isn't set until I actually click on one of the IBAction buttons and manually trigger a change.

I suspect this is some kind of layer drawing bug where the RolePill class is redrawing every time my NSTableView reloads, but I don't know how to get it to accept the initial role state sent in tableView viewForRow.

Any idea what I'm doing wrong here? Thanks!

1

There are 1 answers

2
Clifton Labrum On

I was able to get this to work by setting all the NSView properties inside setRole() like this:

view.wantsLayer = true
view.layer?.cornerRadius = 11
view.layer?.borderWidth = 2
view.layer?.borderColor = getAccentColor().cgColor

label.textColor = Color.white

It seemed that the draw() method on my RolePill subclass was getting called frequently, so I couldn't set a border color in there since it doesn't know what the user's data state is.