focus a field in a NSTableCellView for instant editing - swift

I want to set the focus on one of two editfields in a NSTableCellView in a table with one column in a row programaticly, so that a user can instantly edit the field.
In my example I can select the row (after pressing a button), but I cann´t find a way to set the focus on a special field.
I my example I want to set the cursor on the tiltle field in row 3.
class MainVC: NSViewController, NSTableViewDataSource, NSTableViewDelegate
{ var t = [Item]()
#IBOutlet weak var itemTV: NSTableView!
#IBAction func actionBu(_ sender: Any)
{
let indexSet = IndexSet(integer: 2)
itemTV.selectRowIndexes(indexSet, byExtendingSelection: false)
itemTV.scrollRowToVisible(2)
}

thanks to all helping commands, I found an solution
#IBAction func actionBu(_ sender: Any)
{ self.view.window!.makeFirstResponder(itemTV.view(atColumn: 0, row: 2, makeIfNecessary: true)?.subviews[0])
}
works fine for me,

Related

NSTableView.setNeedsDisplay() not redrawing on attached Formatter changes only

i am using a view based NSTableView with a column that shows dates, and the table cell views use a shared DateFormatter.
let view: NSTableCellView? = tableView.makeView(withIdentifier: column.identifier, owner: self) as! NSTableCellView?
let entry = (logController.arrangedObjects as! [LogEntry])[row]
switch column.identifier {
case columnDateKey:
view?.textField?.formatter = sharedDateFormatter
view?.textField?.objectValue = entry.date
The application has a user preference to choose the date format and previously the code
tableView.setNeedsDisplay(tableView.rect(ofColumn: tableView.column(withIdentifier: columnDateKey)))
would refresh the column with the new date format.
With macOS Mojave this does not happen. Investigation shows that although the drawRect: is called for the underlying TableView there are no calls made to tableView(:viewFor:row:) to obtain the new values for table cell views. Calling tableView.reloadData(forRowIndexes:columnIndexes:) does result in calls to tableView(:viewFor:row:) but the display does not refresh (although it does for tableView.reloadData()).
Any external cause to redraw e.g. selecting a row correctly updates that area alone. The other thing I've seen is that with a long table slowly scrolling up will eventually result in the new format appearing although existing cells do not change when scrolled back to until scrolled a long way past before returning. This would seem to infer that there are cached views that are not considered to have changed when only the configuration of the attached formatter changes (although are when the value of the contents changes)
This behaviour changed with the introduction of Mojave and I am finding it difficult to believe that no-one else has reported it and so am beginning to question my original code. Am I missing something?
The following test code demonstrates the problem, the "View requested" message is not printed for variants of setNeedsDisplay and display is only redrawn for reloadData()
styleButton is tick box to toggle number format and refreshButton is action button to request a redraw
Setting the value to a random value will result in expected redraw behaviour
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
#IBOutlet weak var table: NSTableView!
#IBOutlet weak var styleButton: NSButton!
#IBOutlet weak var refreshButton: NSButton!
#IBOutlet weak var testView: NSView!
let numberFormatter = NumberFormatter()
func applicationWillFinishLaunching(_ notification: Notification) {
numberFormatter.numberStyle = symbolButton.state == NSControl.StateValue.on ? NumberFormatter.Style.decimal : NumberFormatter.Style.none
}
#IBAction func refresh(sender: Any?) {
numberFormatter.numberStyle = styleButton.state == NSControl.StateValue.on ? NumberFormatter.Style.decimal : NumberFormatter.Style.none
table.setNeedsDisplay(table.rect(ofColumn: 0))
// table.needsDisplay = true
// table.reloadData(forRowIndexes: IndexSet(integersIn: 0..<table.numberOfRows), columnIndexes:[0])
// table.reloadData()
}
}
extension AppDelegate: NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
if tableView == table {
return 40
}
return 0
}
}
extension AppDelegate: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
print("View requested")
guard tableColumn != nil else {
return nil
}
let column = tableColumn!
if tableView == table {
let view: NSTableCellView? = tableView.makeView(withIdentifier: column.identifier, owner: self) as! NSTableCellView?
view?.textField?.formatter = numberFormatter
view?.textField?.objectValue = 123.456
return view
}
return nil
}
}
Incorrectly relying on view.setNeedsDisplay to automatically update subviews. This is not the case (although had appeared to work that way, previously) - refer comment from Willeke above

swift unhide buttons in one column if mouse is over row

I working with swift 4 for osx.
I have a view based NSTableView with 4 columns.
the cells in each column has got the same custom cell class:
class CustomCell: NSTableCellView {
#IBOutlet weak var btnInfo: NSButton!
private var trackingArea: NSTrackingArea!
override func awakeFromNib() {
super.awakeFromNib()
self.trackingArea = NSTrackingArea(
rect: bounds,
options: [.activeAlways, .mouseEnteredAndExited],
owner: self,
userInfo: nil
)
addTrackingArea(trackingArea)
}
override func mouseEntered(with event: NSEvent) {
super.mouseEntered(with: event)
btnInfo.isHidden = false
}
override func mouseExited(with event: NSEvent) {
super.mouseExited(with: event)
btnInfo.isHidden = true
}
}
Now i would like to realize the following situation:
if the user goes with the mouse over a row, the btnInfo should be visible and hide again, it the mouse leaves the row.
problem is (with the code above), that my apps crashes, because btnInfo will be nil
Logically: Because this button is only in column 4 available.
in all other columns it will be nil.
how can i solve this?
The solution is to add an NSTrackingArea to the entire view, not the individual cells. Then on the entire table view, you can get the mouse move events, take the NSEvent's locationInWindow. Then NSTableView has a method row(at point: NSPoint) -> Int that can get you the current row that should be highlighting the button.

How to sort NSTableView with NSArrayController programmatically?

I have a NSTableView with just one column but different values in subviews. The tableView is populated by NSArrayController which is bound to a CoreData entity.
I was wondering how to sort the tableView by custom buttons.
class ViewController: NSViewController {
#IBOutlet var arrayController: NSArrayController!
#IBAction func sortByName(_ sender: Any) {
arrayController.sort... ?
}
#IBAction func sortByAge(_ sender: Any) {
arrayController.sort... ?
}
}
I know how to make NSArrayController to sort the columns by clicking on the row header. But since I have only one column but different values, I´m trying to find a way to sort by custom buttons.
Any help would be much appreciated.
Set the sortDescriptors property of the array controller and call arrayController.rearrangeObjects()
class ViewController: NSViewController {
#IBOutlet var arrayController: NSArrayController!
#IBAction func sortByName(_ sender: Any) {
sortArrayController(by: "name")
}
#IBAction func sortByAge(_ sender: Any) {
sortArrayController(by: "age")
}
func sortArrayController(by key : String) {
arrayController.sortDescriptors = [NSSortDescriptor(key: key, ascending: true)]
arrayController.rearrangeObjects()
}
}

Delete a row from table view using custom button - Swift 3

How to delete a row from tableview using custom buttom
//CustomCell.swift
protocol FavoriteCellDelegate {
func deleteButton(sender:CustomCell)
}
class FavoriteItemTableViewCell: UITableViewCell{
var delegate: FavoriteCellDelegate!
#IBAction func deleteButton(_ sender: UIButton) {
delegate.deleteButton(sender: self)
}
}
CustomClass:UITableViewDataSource,UITableViewDelegate,CustomCellDelegate{
#IBOutlet weak var tableView: UITableView!
// all necessary functions for table view....
// Function delegated to perform action.
func deleteButton(sender:FavoriteItemTableViewCell){
//How should I delete. How can I get index path here
}
}
Q. What should I write in the deleteButton function ? I am unable to get the indexPath here so what should I do instead. I already have another button in cell and the delegation is working fine.
you can get indexPath using table view point like this
let buttonPosition : CGPoint = sender.convert(sender.bounds.origin, to: tableview)
let indexPath = tableview.indexPathForRow(at: buttonPosition)

Getting Ordered TextField Data From Dynamically Created Cells in Swift (3.1) Table View

I am trying to figure out how to get textfield data from cells in a tableview in an ordered fashion. So far I am able to type into each textfield a retrieve the type data. To do so in the correct order though the user must edit the first cell...last cell, any other way and the data isn't in the correct order.
For Example:
I have the program create 5 cells with a textfield,
textfield1: I typed here second
textfield2: I typed here fourth
textfield3: I typed here first
textfield4: I typed here fifth
textfield5: I typed here third
the way I currently have it my dataArray would look identical to this one, because it is being stored based on when it is typed in and not the order of the cells.
I would like to type the above example, but my data come out like this:
textfield1: I typed here first
textfield2: I typed here second
textfield3: I typed here third
textfield4: I typed here fourth
textfield5: I typed here fifth
Here is my textfield editing code:
#IBAction func TitleEditBegin(_ sender: UITextField) {
}
#IBAction func TitleEditEnd(_ sender: UITextField) {
print(sender.tag) // Debug
titleArray.append(sender.text!)
}
I know for the time being that any other changes will be appended to the titleArray, but I want to solve the ordering issue first.
Thanks!
EDIT: I forgot to add in how I am creating the cells, the code is below:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = TitleSessionTableView.dequeueReusableCell(withIdentifier: textCellIdentifier, for: indexPath) as! TitleSessionCell
cell.SessionTitleLabel.text = "Title"
// cell.SessionTitleField.text = "Default"
cell.SessionTitleField.tag = indexPath.row
cell.SessionTitleField.delegate = self
print(indexPath.row) // Debug
return cell
}
EDIT 2: Adding where I define the text fields.
import Foundation
import UIKit
class TitleSessionCell: UITableViewCell{
#IBOutlet weak var SessionTitleField: UITextField!
#IBOutlet weak var SessionTitleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
}
The easiest way would be to use a dictionary I believe. I would assign all of the textFields a different tag. So for textField1 you would say textField1.tag = 1. I don't see the creation of your text fields, so it is hard to show the best way of adding that in, but then after handling that, I would create the dictionary as a class variable.
var textFieldDictionary: [Int: String] = [:]
and then add in the text to it like so:
if sender.text != nil && sender.text != "" {
textFieldDictionary[sender.tag] = sender.text
}
then when you want to retrieve the information, do something like this:
for i in 0..<biggestTextFieldNumber {
if let text = textFieldDictionary[i] {
//do something with text
print(text)
}
}
or you could just grab the specific number values out whenever you needed them by using:
textFieldDictionary[numberYouWant]
I hope this helps!