I'm working on an app with NSOutlineView.
The outline view is view-based and is initialized programmatically like:
internal lazy var outlineView: NSOutlineView = {
let outlineView = NSOutlineView()
outlineView.usesAutomaticRowHeights = true
let column = NSTableColumn(identifier: .listOutlineColumn)
outlineView.addTableColumn(column)
outlineView.outlineTableColumn = column
outlineView.headerView = nil
outlineView.backgroundColor = .clear
outlineView.indentationPerLevel = 16
outlineView.allowsEmptySelection = false
outlineView.refusesFirstResponder = true
outlineView.style = .sourceList
return outlineView
}()
The problem is when I'm editing a row like List 11, if I tap the row New List 2 the new row is in editing mode immediately (and all text is selected).
What I want is just to end the editing and select the new row.
Could somebody tell me how to do this?
Found the answer, if the refusesFirstResponder of outline view is set to false, this property of NSTextField inside the NSTableCellView should also be set to false.
And if a customized cell of NSTextField is used, this refusesFirstResponder property should also be set.
Related
How To Display Dynamic Buttons Text Values in an app.scrollViews?
I would like to able to tap the button inside first row in the scrollViews, but not sure what the index of the button is. I tried the 1, 2 and 3 with no luck.
let scrollViewsQuery = app/*#START_MENU_TOKEN#*/.scrollViews/*[[".otherElements[\"Tabbar\"].scrollViews",".scrollViews"],[[[-1,1],[-1,0]]],[0]]#END_MENU_TOKEN#*/
let elementsQuery = scrollViewsQuery.otherElements
elementsQuery.buttons.element(boundBy: 0).tap() //
print("----------------------------------------------")
var i = 0
for element in elementsQuery.buttons.allElementsBoundByIndex{
i += 1
print(i)
print(element) //How To Display the Button Text here?
// print( elementsQuery.buttons.element(boundBy: i))
}
Assuming you only have one scrollView present, the code to tap the first button in it would be the following:
let myScrollView = app.scrollViews.firstMatch
let myScrollViewsButtons = myScrollView.buttons
let myScrollViewsFirstButton = myScrollViewButtons.firstMatch
myScrollViewsFirstButton.tap()
A button in this context is an XCUIElement, not something that is particularly printable. Buttons do have label attributes that are generally the text displayed on them...
I'm trying to craft a view based tableView delegate method to alter a column's font IFF its identifier is 'name' and its value matches some global; otherwise the the nib should prevail. I think I have either a noob setup issue or my logic is omitting something.
I have two tables, one view based (tag=0), one cell (tag=1), and both have the same view controller as their delegate. Each tableView's has a unique tag and identifier, an array controller for column value bindings, and each of fits columns has an explicit unique identifier - matching its binding; I had tried omitting this - "the automatic setting" - but this yielded no data!?
So, I added this method, but both tableViews call into it; I would expect only the first. The 2nd table is a detail of the 1st, having its 1st array controller selection.
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let item = ((tableView.dataSource as! NSArrayController).arrangedObjects as! [AnyObject])[row]
var view = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self)
if view == nil {
Swift.print("tag: \(tableView.tag) ident: \(tableColumn!.identifier.rawValue)")
view = NSTableCellView.init()
view?.identifier = tableColumn?.identifier
}
guard tableView.tag == 0 else { return view }
guard isGlobalPlist, item.name == '<some-global-string>',
let identifier = tableColumn?.identifier,
identifier.rawValue == "name" else { return view }
if let cellView = view as? NSTableCellView {
if tableView.selectedRowIndexes.intersection(IndexSet.init(integer: row)).count > 0 {
cellView.textField?.font = .boldSystemFont(ofSize: -1)
cellView.textField?.textColor = .white
}
else
{
cellView.textField?.font = .systemFont(ofSize: -1)
cellView.textField?.textColor = .blue
}
}
return view
}
Firstly I notice that the method is called for the 2nd table when I wouldn't have expect it, it being a cell based tableView.
Anyway, I do create the cell when nil return, the docs cite that you need just need to matchup the identifiers:
Note that a cell view’s identifier must be the same as its table column’s identifier for bindings to work. If you’re using bindings, it’s recommended that you use the Automatic identifier setting in Interface Builder.
which I do; both tableView's columns are bound (view, or cell) to their respective array controller using column identifier explicitly entered.
Anyway the original intent I was after was for the view based (1st) tableView:
the view controller was controlling a global resource (isGlobalPlist is true)
the row's 'name' value matches a global value value
to the affect 'name' cell shown in distinct color (blue/yellow - or something) depending on whether the row was selected due to selection highlighting, dark mode etc conditions.
So there would be 1 of 4 possibilities
selected row, matching global -> fore:yellow, back:default
selected row, not matching global -> fore:default, back:default
non-selected row, matching global -> fore:blue, back:default
non-selected row, not matching global -> fore:default, back:default
If the target row matched requirements, the coloring needs to vary depending on its inclusion in the tableView's selected indexes.
The setting seems to properly affect initially - selected row and matching value, but then settings seem to bleed across other rows navigated to - so the selection change. At most there should be 1 row's name column with this affect.
I can't tell if my logic or setup is wrong; I suspect I probably need to refresh as I move about ?
Well I gave up on a view based solution but this fills my need; a cell based solution:
func tableView(_ tableView: NSTableView, dataCellFor tableColumn: NSTableColumn?, row: Int) -> NSCell? {
guard let column = tableColumn else { return nil }
let item : AnyObject = ([table0ArrayController,table1ArrayController][tableView.tag]?.arrangedObjects as! [AnyObject])[row]
let cell : NSTextFieldCell = column.dataCell(forRow: row) as! NSTextFieldCell
cell.textColor = .textColor
guard tableView.tag == 0, isGlobalPlist, item.name == '<some-global-string>' else { return cell }
if tableView.selectedRowIndexes.intersection(IndexSet.init(integer: row)).count > 0 {
cell.textColor = .white
}
else
{
cell.textColor = .blue
}
return cell
}
I wanted to call attention to specific row(s) with color, unless row was selected for the 'name' column only.
I have a single column, view-based NSTableView in which I want to place two different custom cell views, one alternating with the other in each row, like so:
Odd rows: OddRowNumberCellView.
Even rows: EvenRowNumberCellView.
There a solution for iOS from Natasha in her highly rated answer UITableview with more than One Custom Cells with Swift but it depends on the Dynamic Prototypes setting in the Attributes Inspector, a feature that is not available for MacOS.
Does anyone know how to do this on Mac please? (I'm on macOS Sierra 10.12.4.)
You do exactly the same but instead of Dynamic Protoypes you set an Identifier "OddRow" or "EvenRow" and then in your datasource implementation:
if indexPath.row % 2 == 0 {
let cellView: tableView.make(withIdentifier: "EvenRow")
//set the data here
return cellView
} else {
let cellView: tableView.make(withIdentifier: "OddRow")
//set the data here
return cellView
}
I've a really simple UI with a single NSPopUpButton. Its selectedIndex is bound to an int value ViewController.self.my_selection. As soon as I change the selection from the UI (i.e. a select the third item of the NSPopUpButton) I see that my_selection value changes. So far so good, what I'm trying to obtain is the opposed direction though. I want to change the my_selection value programmatically and see the NSPopUpButton selecting the item a the index that I've defined in my_selection. I erroneously supposed that behaviour was the default behaviour for bindings...
This is what I'm obtaining now:
NSPoPUpButton ---> select item at index 2 ----> my_selection becomes equal to 2
This is what I want to achieve (keeping also the previous behaviour)
my_selection ---> set value to 3----> NSPoPUpButton selected index = 3
Without a bit more info (see my comment) it's hard to see exactly what you're doing wrong. Here's how I got it working: First, create a simple class...
// Create a simple class
class Beatle: NSObject {
convenience init(name: String) {
self.init()
self.name = name
}
dynamic var name: String?
}
Then, in the AppDelegate I created a four-item array called beatles:
dynamic var beatles: [Beatle]?
func applicationDidFinishLaunching(aNotification: NSNotification) {
beatles = [Beatle(name: "John"),
Beatle(name: "Paul"),
Beatle(name: "Ringo"),
Beatle(name: "George")]
}
In Interface Builder I set things up so that this array provides the pop-up with its content:
This class also has a selectedIndex property that is bound to the pop-up button's selectedIndex binding - this property provides read-write access to the pop-up button's selection:
// Set the pop-up selection by changing the value of this variable.
// (Make sure you mark it as dynamic.)
dynamic var selectedIndex: Int = 0
I've got a simple example of an app here which I slapped together, and what I'm getting is pretty much what I'm after.
The issue is that when the view loads up, in the NSViewController's viewDidLoad, I set the tableView's selected index to 0 i.e. the first item (which works).
What I do notice is that when this happens, the selected row comes up as grey in color (i.e. as if it's not an active window/view)… It only seems to high light in the normal blue color when I physically click on the row that's selected.
I can confirm that the row is selected and everything appears fine.
Any ideas?
To confirm, the code I use to select the row is:
override func viewDidAppear() {
self.tableView.selectRowIndexes(NSIndexSet(index: 0), byExtendingSelection: false)
}
Here is what's happening with the actual view itself:
ABOVE: The darker grey line is the "selection bar". This is what happens as soon as the view becomes active.
ABOVE: Once I click on that row (the one which was once dark grey), I get he desired high lighting.. i.e. Navy Blue.
The reason why the cell is grey is because the table view doesn't have focus / isn't the first responder.
There are 3 states for tableView cell selection color
no selection = clear row background
selection and focus = blue row background
selection and no focus = grey row background
This is probably because another view has focus. Simply selecting a cell doesn't shift focus to a tableView. You need to call NSWindow.makeFirstResponder() to change the focus.
func tableViewSelectionDidChange(notification: NSNotification) {
let tableView = notification.object as! NSTableView
if tableView.selectedRow != -1 {
self.window!.makeFirstResponder(self.tableView)
}
}
I've managed to find out what's going on. (I think) and it seems to work.
I had to:
Subclass NSTableRowView
Add a new NSView just below the actual cell view (row) in Interface Builder
Set the new Row View's class to 'myNSTableViewSubClass'
Set the row view's Identifier to: NSTableViewRowViewKey (this is very specific, and that literally is the key, if this isn't set, it won't work be regarded as the Table Row View.
in the subclass I had to override the emphasised: Bool to always return yes e.g.:
override var emphasized: Bool{
get{
return true
}
set{
//You need to have the "set" there as it's a mutable prop
//It doesn't have to do untying though
}
}
And voila..
The catch in my case was in 4 above.