Swift & NSTableView - Simply select a row - xcode

I'd like to programatically select a row in my NSTableView, with just one column.
func selectColumnIndexes(_ indexes: NSIndexSet!,
byExtendingSelection extend: Bool)
I have been playing around with this, but I am not sure how to write "Select row #2".
Do I have to start with the variable connected to the #IBOutlet of my Table View? I have to indicate it is about my NSTableView somehow.
Thanks!

#Isaiah answer in Swift 3.0:
.selectRowIndexes(NSIndexSet(index: 0) as IndexSet, byExtendingSelection: false)

Okay, I've got it. It turned out to be
#IBOutlet weak var NoteTableView: NSTableView!
NoteTableView.selectRowIndexes(NSIndexSet(index: 0), byExtendingSelection: false)
I couldn't quite figure out the part with NSIndexSet. I was fiddling around with init(), which turned out to be unnecessary.

Swift 4 Solution
I created an extension to make a little easier to do this. Also if you implemented a click action on the table view cell this will invoke it as well
extension NSTableView {
func selectRow(at index: Int) {
selectRowIndexes(.init(integer: index), byExtendingSelection: false)
if let action = action {
perform(action)
}
}
}
/// USAGE:
NSTableView().selectRow(at: 0)

Related

UIPickerView rendering incorrectly after iOS 14/Xcode 12.0.1 update

Anybody notice that the text in your application's UIPickerViews is rendered incorrectly, with the first character cut off? I'm seeing this in all UIPickerViews in my app, on multiple devices. You can see a few pixels of the first character in most cases.
I've tried deleting derived data, and the application from the phones, but no dice.
I'm not sure which update might have triggered the problem, but it just started in a project that has been stable for months. The code for the labels:
func pickerView(_ pickerView: UIPickerView,
viewForRow row: Int,
forComponent component: Int,
reusing view: UIView?) -> UIView
{
let pickerLabel = UILabel()
pickerLabel.text = "Rec.709"
pickerLabel.font = UIFont(name: "Ropa Sans", size: 18)
pickerLabel.textColor = UIColor.white
pickerLabel.textAlignment = NSTextAlignment.left
}
Did not change anything to my PickerViews, but I am experiencing exactly the same problem since updating to iOS 14. Seems like Apple changed something in the PickerView implementation.
My ViewForRow function is returning a horizontal UIStackView containing three labels. I was able to solve the problem temporarily by adding a 15 points offset constraint to the leading edge of the first label, and the trailing edge of the last:
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
var stackView: UIStackView
if view != nil {
stackView = view as! UIStackView
} else {
let leftLabel = UILabel()
let ctrLabel = UILabel()
let rightLabel = UILabel()
stackView = UIStackView(arrangedSubviews:[leftLabel, ctrLabel, rightLabel])
stackView.axis = .horizontal
stackView.distribution = .fill
// Temporary fix.
leftLabel.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 15.0).isActive = true
rightLabel.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -15.0).isActive = true
// Set text of labels here...
return stackView
}
I haven't been able to check yet, but I am afraid that this extra margin may now look weird on devices that are still running an older iOS version.
I found only the leftmost component's left-aligned string to be cut off (for example, I have a multipicker view with 3 components, all of which are left-aligned. Only the leftmost component is cutoff.
I found it easier to just modify the strings to pad them with a couple of extra leading spaces - I'm using a dictionary for the strings and so they are all in one place (and used only for display, so I don't have to worry about using values). Worked well in my case. If and when Apple fixes the issue, it will be easy to revert (if necessary).

tableView(tableView, viewFor:,: Int) strange behavior

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.

get row number of dragged row in tableview

i have two table views where you can drag a row from table A to table B.
this works fine.
but i need to know, which row number will dragged from table A.
This is my code of table A and the problem, too:
func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
pboard.declareTypes([NSStringPboardType],owner: nil)
pboard.setString(persons[0].objectID.uriRepresentation().absoluteString, forType: NSStringPboardType)
return true
}
at the moment i always dragged the first element of "persons"
i need a solution where i can set a row number (or something like this) instead of the static "persons[0]".
is there an way for this?
Have you tried persons[rowindexes.first] ?

xcode 7.3 reference icon from assets folder for prototype table view cell

In Xcode 7 with swift, I'm trying to use icons from my assets folder to display in the icon slot in a normal prototype tableview cell.
My code compiles and the app runs fine, displaying all the proper row text titles in the table, but all the cells use an icon that I (perhaps stupidly) entered into the attributes inspector for the prototype table cell.
By default, the style=Basic table cell does not have an icon to the left of the Title in the storyboard. BUT... if you add the name of an icon image from the assets folder in the image=MyAssetsIconName, the UI builder automatically adds a UIImageView to the left end of the table row cell. And of course all rows display the image that you selected. Which is what my app currently does.
But I would like to change the icon image for each row, so that the image on any row matches the text rows that I display. So I tried to assign a new image (from the assets folder) to each row cell as it was created. I did the assignment right after I assigned the text title to the cell.
Here is my code, which runs fine (but doesn't display the images that I want).
class TsbReportsCell: UITableViewCell {
#IBOutlet weak var Title: UILabel!
#IBOutlet weak var Detail: UILabel!
#IBOutlet weak var Icon: UIImageView!
...
}
In the table view controller:
In the table view controller class:
// define datasources
var reportnames = [String]()
var reportimages = [String]()
override func viewDidLoad() {
super.viewDidLoad()
reportnames = ["Balance Report",
"Routine Report",
"Low Quality Report"]
reportimages = ["enter.png",
"exterior.png",
"export.png"]
}
override func tableView(tableView:.... {
// all this code works fine
let cell = tableView.dequeueReusableCellWithIdentifier...
let row = indexPath.row
cell.Title.text = reportnames[row]
// here are the lines that seem to have no effect at all
let rowimage = UIImage(named: reportimages[row])
cell.Icon = UIImageView(image: rowimage)
return cell
}
Any ideas what I'm doing wrong? I named an image in the attributes for the table cell row just to get the UI builder to add the imageview placeholder for me. But maybe I'm not allowed to ever override that image, even though I can create an outlet for it.
This seems so simple a problem, but I searched at least 20 posts, the net, the doc, and I still couldn't find anything. Thanks
It's very simple. You are saying:
cell.Icon = UIImageView(image: rowimage)
But that does not put this image view into the interface! It merely assigns it to the cell's property.
Instead, you need to have an image view in the cell and hook an outlet from the cell's Icon property to that image view. As a result, cell.Icon is the image view in the interface. Now you say
cell.Icon.image = rowimage
Man, are you ever fast at answering questions, Matt. I'm sure I'm not the only one who appreciates that.
It's funny how taking a half hour to write up a decent question for this site makes you start thinking about other things to try. I took a seg fault on one of my tries, and was poking around in the error messages. From them, I got the idea that an ImageView was a structure that had an "image" field in it (or something like that), so I tried some UIImageView x UIImage crosses to see what would happen.
Sure enough, I was trying to assign an ImageView to an Image (I think; I could still be wrong). Here's the code line that worked for me.
cell.Icon.image = UIImage(named: reportimages[row])
So you can see I'm assigning an image to an image here, which works properly. AND... it must be right, because it's the same code as Matt's!! :-)

Can't setup bindings in Swift Storyboard?

I've got some code that runs a fairly complex algorithm. I want to put together a fairly simple UI that will allow me to monitor the values of the various variables in the algorithm in some graphical ways -- think of it like a dashboard of sorts.
So, for simplicity's sake, let's say I have an algorithm like what follows. It searches a vector of values for the two values that most closely sum to a target value:
import Foundation
class algorithm {
var numbers = [Double]()
let numberOfRandoms = 1000
dynamic var a: String
dynamic var b: String
init () {
// Load initial vector with some random numbers between 0 and 1
for _ in 1...numberOfRandoms {
numbers.append(Double(arc4random()) / Double(UINT32_MAX))
}
a = " "
b = " "
}
func findTheTwoNumbersThatAddUpTheClosestToTarget(target: Double) {
//Initializing this to a very large value
var currentBestSum = 1000.0
//Begin brute force search for the optimal solution
for i in 0...numbers.count-2 {
for j in i+1...numbers.count-1 {
//Check to see if the current candidate exceeds the best solution
if abs(numbers[i] + numbers[j] - target) < currentBestSum {
//If it does, store the new champion
a = String(i)
b = String(j)
//And reset the current top score to match
currentBestSum = abs(numbers[i] + numbers[j]-target)
}
}
}
}
}
Now, this is just a simple (and silly) example, but it suits these purposes. I basically want to create a simple UI that displays the important values in the process as it runs (dynamically).
In this example, let's say that I just want to display two labels that contain the index values of the two leaders as the algorithm executes.
I created the labels in the storyboard.
Then, I created IBOutlets in the ViewController (Actually, storyboards did it for me when I Ctrl-dragged):
class ViewController: NSViewController {
#IBOutlet weak var a: NSTextField!
#IBOutlet weak var b: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
Then, I ctrl-dragged the labels to the a and b in the algorithm class to create the bindings.
Finally, I create an class variable in the view controller and instantiate it in the viewDidLoad method. This doesn't seem like the right thing to do -- maybe it is. Seems like you would want to keep separate your interface and data...
The labels do, in fact, show up -- but they never show any values of a and b. They just show the default text.
Not sure what I'm doing wrong.
Help!?
P.S., in response to Anthony Kong, I do recognize that I could manually synchronize all the view elements in the code, but I thought the whole point of using bindings was to avoid having to do this manual synchronization. I just can't figure out how to set it up.
Without commenting on your specific code I think I have experienced (and solved) the problem you describe. I was able to write an app that had two targets, one NIB-based and one Storyboard-based. As much as I was able I duplicated the code in each and shared the common data instance that I was trying to display in a TableView. The NIB-based app worked using the stock Cocoa Bindings that I set in IB. But the Storyboard-based app did not, the array controller did not see the data.
My solution was simply to add the binding for contentArray programmatically in viewDidLoad. The one line that fixed it for me is:
ac.bind("contentArray", toObject: cd, withKeyPath: "people", options: nil)
ac is the IBOutlet for the ArrayController in the Storyboard. cd is the class instance that contains the people array.
This is using XCode 6.2 (6C107a) which is Beta 3 I think.
This was the only binding that I had to set myself, the TableView to ArrayController (arrangedObjects) and TableViewCell to TableView (objectValue) didn't need any tweaking.
There are several problems with your code
1) In your code,
func applicationDidFinishLaunching(aNotification: NSNotification?) {
// Insert code here to initialize your application
var test = algorithm()
test.findTheTwoNumbersThatAddUpTheClosestToTarget(0.5)
}
The variable test goes out of scope when the function exits. Based on the wording of your question, you are expecting it to be a long running process. So this will not do what you want.
You should
a) Add test as class variable to the ViewController class
b) instantiate the variable in viewDidLoad method.
2) In your algorithm it does not actually provide any feedback to the labels. Maybe you think because the class has the ivar a and b so they are hooked to the IBOutlet by the same names. But of course it is not the case. And you do not need the keyword dynamic too.
What you should do is:
a) provide a method in the View Controller class to update the labels. It will serve as a callback function to be used by algorithm class to feedback the calculation result.
It may look like this:
func update_value_callback(vala: String, valb: String) {
a.text = vala; // updating the label here
b.text = valb;
}
b) make the algorithm class calls the callback function e.g.
func findTheTwoNumbersThatAddUpTheClosestToTarget(target: Double, viewController: ViewController) {
// do your stuff
...
// execute the callback
viewController.update_value_callback(a, b)
}

Resources