Reorder of cell in Collection View in Swift - uicollectionview

I'm using a collection view, inside a cell there is an image view. The images are coming from gallery, now i'm trying to reorder my images. Suppose when i move my image at index 1 to index 0 it moves fine but the issue is that my image in an imageview does not go original which was at index 1, it is changed to an image which is placed at index 0. I have tried this code,
longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.handleLongGesture(gesture:)))
uploadDocCollectionView.addGestureRecognizer(longPressGesture)
#objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {
switch(gesture.state) {
case UIGestureRecognizerState.began:
guard let selectedIndexPath = self.uploadDocCollectionView.indexPathForItem(at: gesture.location(in: self.uploadDocCollectionView)) else {
break
}
uploadDocCollectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
case UIGestureRecognizerState.changed:
uploadDocCollectionView.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
case UIGestureRecognizerState.ended:
uploadDocCollectionView.endInteractiveMovement()
default:
uploadDocCollectionView.cancelInteractiveMovement()
}
}
In my collection these are the function i have called,
func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
print("Starting Index: \(sourceIndexPath.item)")
print("Ending Index: \(destinationIndexPath.item)")
}

Remove the image at selected index and insert to the destination index of the same array
func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let image = arrayOfImages[sourceIndexPath.row]
arrayOfImages.remove(at: sourceIndexPath.row)
arrayOfImages.insert(image, at: destinationIndexPath.row)
collectionView.reloadData()
}

Related

Swift and #objc methods: How do I transform a method so that it can be represented by #objc?

As Swift is my first programming language and also seeing that I have no Objective C experience...
I'm having difficulty understanding #objc in relation to methods.
How do I use the #objc syntax to conform to my methods?
Is there another way to select a method without using the #selector syntax?
Here is the code that I'm having difficulty with(mainly the #objc attempt at the startGame method):
import UIKit
#objc class ViewController: UITableViewController {
var allWords = [String]()
var usedWords = [String]()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem =
UIBarButtonItem(barButtonSystemItem: .add, target: self, action:
#selector(promptForAnswer))
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "New
Word", style: .plain, target: self, action: #selector(startGame))
if let startWordsURL = Bundle.main.url(forResource: "start",
withExtension: "txt") {
if let startWords = try? String(contentsOf: startWordsURL) {
allWords = startWords.components(separatedBy: "\n")
}
}
if allWords.isEmpty {
allWords = ["silkworm"]
}
#objc func startGame() {
title = allWords.randomElement()
usedWords.removeAll(keepingCapacity: true)
tableView.reloadData()
{
startGame()
}
A few observations:
You do not need #objc in your view controller declaration.
The two action/selector methods should bear #objc qualifier.
I would suggest that you give these two methods descriptive names that clearly indicate that they are called when the user taps on a particular button, e.g.:
#objc func didTapNewWord(_ sender: UIBarButtonItem) {
...
}
#objc func didTapAdd(_ sender: UIBarButtonItem) {
...
}
Note, I also added a parameter to these methods. That makes it entirely unambiguous that they are button handlers. You do not need to do that, but now you can glance at the code and immediately grok what the method is for.
Obviously, you will change the code that adds these target actions accordingly:
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add,
target: self,
action: #selector(didTapAdd(_:)))
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "New Word",
style: .plain,
target: self,
action: #selector(didTapNewWord(_:)))
Be careful with the placement of braces. Swift allows you to declare functions inside functions. So make sure that these selector methods are instance methods of the view controller, and not, for example, private functions declared inside another function (i.e. viewDidLoad).
If you start to lose track of the braces, you can select all the code in this file and press control+i (or in Xcode menus, “Editor” » “Structure” » “Re-Indent”). If you have missing braces somewhere, the re-indentation of the code will make this jump out at you.
So pulling that together, you get something like:
// ViewController.swift
import UIKit
class ViewController: UITableViewController {
var allWords = [String]()
var usedWords = [String]()
override func viewDidLoad() {
super.viewDidLoad()
configureButtons()
fetchData()
}
}
// MARK: - Actions
extension ViewController {
#objc func didTapNewWord(_ sender: UIBarButtonItem) {
startGame()
}
#objc func didTapAdd(_ sender: UIBarButtonItem) {
...
}
}
// MARK: - UITableViewDataSource
extension ViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
...
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
}
}
// MARK: - Private utility methods
private extension ViewController {
func configureButtons() {
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add,
target: self,
action: #selector(didTapAdd(_:)))
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "New Word",
style: .plain,
target: self,
action: #selector(didTapNewWord(_:)))
}
func fetchData() {
guard
let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt"),
let startWords = try? String(contentsOf: startWordsURL).components(separatedBy: "\n"),
!startWords.isEmpty
else {
allWords = ["silkworm"]
return
}
allWords = startWords.filter { !$0.isEmpty }
}
func startGame() {
title = allWords.randomElement()
usedWords.removeAll(keepingCapacity: true)
tableView.reloadData()
}
}
A few final observations on my code sample (not directly related to your question, but just to explain why structured it like I did):
I like to put methods into extensions, so that they are in logical groups. This makes it easier to follow what is going on at a glance. You can also collapse/expand these extensions so that while you are editing, you can focus on the relevant code.
The MARK comments just puts nice section headers in the Xcode jump bar, again, making it easier to jump about in one’s code.
I personally don't put anything in the action methods except a call to some method with the “business logic”. This separates the “view” code (the handling of the button) from the business logic. Some day, you may start using view models or presenter objects, so embracing this separation of responsibilities now will make that eventual transition easier. It will also make it easier to write unit tests when you get around to that (e.g. you write unit tests for the "start game" logic, not not the tapping of a button).
I think you have syntax error in the #objc method. It should be:
#objc
func functionName() {
}
for you it will be:
#objc
func startGame() {
title = allWords.randomElement()
usedWords.removeAll(keepingCapacity: true)
tableView.reloadData()
}

Applying NSDiffableDataSourceSnapshot to UICollectionViewDiffableDataSource causes 'NSInternalInconsistencyException'

I am trying to implement a UICollectionViewDiffableDataSource for my collectionView. My code compiles fine, however I keep running into this error the first time I apply a snapshot to it, with the following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: self.supplementaryViewProvider || (self.supplementaryReuseIdentifierProvider && self.supplementaryViewConfigurationHandler)'
Here is my code:
var groups: [Group] = [Group]()
var dataSource: UICollectionViewDiffableDataSource<Section, Group>!
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar.delegate = self
self.groups = DummyData.groups
setupDataSource()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
performSearch(searchQuery: nil)
}
// MARK: - Helper Functions
func performSearch(searchQuery: String?) {
let filteredGroups: [Group]
if let searchQuery = searchQuery, !searchQuery.isEmpty {
filteredGroups = groups.filter { $0.contains(query: searchQuery) }
} else {
filteredGroups = groups
}
var snapshot = NSDiffableDataSourceSnapshot<Section, Group>()
snapshot.appendSections([.main])
snapshot.appendItems(filteredGroups, toSection: .main)
dataSource.apply(snapshot, animatingDifferences: true, completion: nil)
}
func setupDataSource() {
dataSource = UICollectionViewDiffableDataSource <Section, Group>(collectionView: collectionView) { (collectionView: UICollectionView, indexPath: IndexPath, group: Group) -> UICollectionViewCell? in
guard let cell = self.collectionView.dequeueReusableCell(
withReuseIdentifier: String(describing: MyGroupsCollectionViewCell.self), for: indexPath) as? MyGroupsCollectionViewCell else {
fatalError("Cannot create new cell") }
cell.configure(withGroup: group)
return cell
}
}
If needed, I can post the full call stack.
Found the answer. I was using the storyboard to create my collectionView and accidentally had the attribute for Section Header set to true. Because of this, the collectionView needed to pull the view for the section header for somewhere, but I never told it where, hence the
parameter not satisfying: self.supplementaryViewProvider || (self.supplementaryReuseIdentifierProvider && self.supplementaryViewConfigurationHandler)
Here's a good article I found on it for anyone in the future who runs into this issue:
https://medium.com/#jamesrochabrun/uicollectionviewdiffabledatasource-and-decodable-step-by-step-6b727dd2485

Autosizing Cells in UICollectionView (all in code)

Using Swift-5.0, Xcode-10.2, iOS-12.2,
There is an issue in my code while trying to achieve "autosizing" of cells in a UICollectionView (using UICollectionViewFlowLayout as its layout).
The height of the cell is content-based (and unknown upfront) - therefore I try to get the cell's height to autosize (the "width" I don't care for now and can, for example, be set to frame.width).
Even tough, I only use one large UICollectionView-Cell for the example below, I would still like to keep "dequeueing" of the cells alive since later on, there will be many more cells that need to be filled with large content. Therefore, to make this example more simple, I keept the numberOfItemsInSection at 1.
Moreover, for the below example, each custom CollectionViewCell is filled with a vertical StackView (vertically adding-up a couple of Labels and ImageViews). The StackView is not important here, but I made it as a quick example. Again, the content of the custom CollectionViewCell will change later on (especially it will change to a content-dependent height). The fixed-height cell-content in the code below is just for example reasons...
But what I would still like to get out of this, is the question on how to make the CollectionViewCell autosize its height according to the content (whether fixed-height like in the below example or dynamic content-based-height like in the future implementation) ???
I keep getting the following Constraint-error:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one
you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
Everyting works, if I give the Cell a very large fixed height using the method sizeForItemAt inside my UICollectionViewController. (i.e. with "very large" I mean much bigger than the cell's height turns out when dequeueing of the cell takes place).
But as explained above, I do not want to set the cell's height to a fixed value (using the UICollectionViewController's method sizeForItemAt - but rather achieve the desired autosizing.
To autosize the cell, I tried the following:
Approach A)
Use collectionViewFlowLayout.estimatedItemSize = CGSize(...)
(and don't use sizeForItemAt method)
--> Again same thing: If the estimated-size is set large enough, no error occurs. If I set it too small, then I get the same Constraint-error...
Approach B)
Use collectionViewFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
(and use or don't use sizeForItemAt method - does not make a difference)
--> Again same error: As soon as I start scrolling on the UICollectionView-cell, it throws the same Constraint-error...
Somewhat promising is the observation that using UICollectionViewFlowLayout.automaticSize makes the App work as desired (except that the above error is still thrown - but strange-enough, the App continues somehow to run anyway). For me it is not acceptable that the App works and an error is thrown. The question is, how to get rid of the Constraint-error ??
Here is all the code that I use:
class TestViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cellID"
override func viewDidLoad() {
super.viewDidLoad()
collectionView.backgroundColor = .yellow
collectionView.register(MyCollectionViewCell.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MyCollectionViewCell
return cell
}
// func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// return .init(width: view.frame.width, height: 4000)
// }
init() {
let collectionViewFlowLayout = UICollectionViewFlowLayout()
// collectionViewFlowLayout.estimatedItemSize = CGSize(width: UIScreen.main.bounds.width, height: 4000)
collectionViewFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
super.init(collectionViewLayout: collectionViewFlowLayout)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is the CustomCollectionViewCell:
import UIKit
class MyCollectionViewCell: UICollectionViewCell {
let titleLabel1 = UILabel(text: "Title 123", font: .boldSystemFont(ofSize: 30))
let titleLabel2 = UILabel(text: "Testing...", font: .boldSystemFont(ofSize: 15))
let imgView1 = UIImageView(image: nil)
let imgView2 = UIImageView(image: nil)
let imgView3 = UIImageView(image: nil)
let imgView4 = UIImageView(image: nil)
let imgView5 = UIImageView(image: nil)
let imageView: UIImageView = {
let imgView = UIImageView()
imgView.backgroundColor = .green
return imgView
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .yellow
titleLabel1.constrainHeight(constant: 50)
titleLabel1.constrainWidth(constant: 250)
titleLabel2.constrainHeight(constant: 50)
titleLabel2.constrainWidth(constant: 250)
imgView1.constrainHeight(constant: 100)
imgView1.constrainWidth(constant: 200)
imgView1.backgroundColor = .green
imgView2.constrainHeight(constant: 100)
imgView2.constrainWidth(constant: 200)
imgView2.backgroundColor = .green
imgView3.constrainHeight(constant: 100)
imgView3.constrainWidth(constant: 200)
imgView3.backgroundColor = .green
imgView4.constrainHeight(constant: 100)
imgView4.constrainWidth(constant: 200)
imgView4.backgroundColor = .green
imgView5.constrainHeight(constant: 100)
imgView5.constrainWidth(constant: 200)
imgView5.backgroundColor = .green
let stackView = UIStackView(arrangedSubviews: [titleLabel1, imgView1, titleLabel2, imgView2, imgView3, imgView4, imgView5])
addSubview(stackView)
stackView.anchor(top: safeAreaLayoutGuide.topAnchor, leading: leadingAnchor, bottom: bottomAnchor, trailing: trailingAnchor)
stackView.spacing = 20
stackView.axis = .vertical
stackView.alignment = .center
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Needed extensions are defined as such:
extension UILabel {
convenience init(text: String, font: UIFont) {
self.init(frame: .zero)
self.text = text
self.font = font
self.backgroundColor = .red
}
}
extension UIImageView {
convenience init(cornerRadius: CGFloat) {
self.init(image: nil)
self.layer.cornerRadius = cornerRadius
self.clipsToBounds = true
self.contentMode = .scaleAspectFill
}
}
To abstract away the anchoring and height-definitions of the autolayout-constraints of the cell's components (i.e. a bunch of labels and imageViews that fill the cell), I used the following UIView extension...:
(The extension has been published by Brian Voong - see video link)
// Reference Video: https://youtu.be/iqpAP7s3b-8
extension UIView {
#discardableResult
func anchor(top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets = .zero, size: CGSize = .zero) -> AnchoredConstraints {
translatesAutoresizingMaskIntoConstraints = false
var anchoredConstraints = AnchoredConstraints()
if let top = top {
anchoredConstraints.top = topAnchor.constraint(equalTo: top, constant: padding.top)
}
if let leading = leading {
anchoredConstraints.leading = leadingAnchor.constraint(equalTo: leading, constant: padding.left)
}
if let bottom = bottom {
anchoredConstraints.bottom = bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom)
}
if let trailing = trailing {
anchoredConstraints.trailing = trailingAnchor.constraint(equalTo: trailing, constant: -padding.right)
}
if size.width != 0 {
anchoredConstraints.width = widthAnchor.constraint(equalToConstant: size.width)
}
if size.height != 0 {
anchoredConstraints.height = heightAnchor.constraint(equalToConstant: size.height)
}
[anchoredConstraints.top, anchoredConstraints.leading, anchoredConstraints.bottom, anchoredConstraints.trailing, anchoredConstraints.width, anchoredConstraints.height].forEach{ $0?.isActive = true }
return anchoredConstraints
}
func fillSuperview(padding: UIEdgeInsets = .zero) {
translatesAutoresizingMaskIntoConstraints = false
if let superviewTopAnchor = superview?.topAnchor {
topAnchor.constraint(equalTo: superviewTopAnchor, constant: padding.top).isActive = true
}
if let superviewBottomAnchor = superview?.bottomAnchor {
bottomAnchor.constraint(equalTo: superviewBottomAnchor, constant: -padding.bottom).isActive = true
}
if let superviewLeadingAnchor = superview?.leadingAnchor {
leadingAnchor.constraint(equalTo: superviewLeadingAnchor, constant: padding.left).isActive = true
}
if let superviewTrailingAnchor = superview?.trailingAnchor {
trailingAnchor.constraint(equalTo: superviewTrailingAnchor, constant: -padding.right).isActive = true
}
}
func centerInSuperview(size: CGSize = .zero) {
translatesAutoresizingMaskIntoConstraints = false
if let superviewCenterXAnchor = superview?.centerXAnchor {
centerXAnchor.constraint(equalTo: superviewCenterXAnchor).isActive = true
}
if let superviewCenterYAnchor = superview?.centerYAnchor {
centerYAnchor.constraint(equalTo: superviewCenterYAnchor).isActive = true
}
if size.width != 0 {
widthAnchor.constraint(equalToConstant: size.width).isActive = true
}
if size.height != 0 {
heightAnchor.constraint(equalToConstant: size.height).isActive = true
}
}
func centerXInSuperview() {
translatesAutoresizingMaskIntoConstraints = false
if let superViewCenterXAnchor = superview?.centerXAnchor {
centerXAnchor.constraint(equalTo: superViewCenterXAnchor).isActive = true
}
}
func centerYInSuperview() {
translatesAutoresizingMaskIntoConstraints = false
if let centerY = superview?.centerYAnchor {
centerYAnchor.constraint(equalTo: centerY).isActive = true
}
}
func constrainWidth(constant: CGFloat) {
translatesAutoresizingMaskIntoConstraints = false
widthAnchor.constraint(equalToConstant: constant).isActive = true
}
func constrainHeight(constant: CGFloat) {
translatesAutoresizingMaskIntoConstraints = false
heightAnchor.constraint(equalToConstant: constant).isActive = true
}
}
Finally, after some more digging, I've found a solution:
Four things are important if you want to autosize your custom UICollectionViewCell:
when creating your UICollectionViewController instance, make sure you pass a FlowLayout having set the estimatedItemSize property to some educated-guess value (in my case with a height = 1 [since unknown])
let collectionViewFlowLayout = UICollectionViewFlowLayout()
collectionViewFlowLayout.estimatedItemSize = CGSize(width: UIScreen.main.bounds.width, height: 1)
let vc = TestViewController(collectionViewLayout: collectionViewFlowLayout)
.
create a property inside your custom UICollectionViewCell that keeps track of the cell's height
(i.e. inside your UICollectionViewCell class, you will always know what the cell's height is (even if dynamically changed later on). Again, I wanted to get rid of the fact that I needed to define this height already at the UICollectionViewController's sizeForItemAt method before dequeueing the cell)
Don't forget to add the preferredLayoutAttributesFitting method inside your custom UICollectionViewCell :
let myContentHeight = CGFloat(720)
// in my above code-example, the 720 are the sum of 2 x 50 (labels) plus 5 x 100 (imageViews) plus 6 x 20 (spacing)
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
setNeedsLayout()
layoutIfNeeded()
var newFrame = layoutAttributes.frame
// myContentHeight corresponds to the total height of your custom UICollectionViewCell
newFrame.size.height = ceil(myContentHeight)
layoutAttributes.frame = newFrame
return layoutAttributes
}
Of course, whenever your custom CollectionViewCell changes its height dynamically, you also need to change the myContentHeight property again...
(the calling of preferredLayoutAttributesFitting you don't need to care yourself in code, this method will be called automatically whenever the user enters the view, scrolls or does anything else. The OS takes more or less care of this...).

Where to Set Column Width in NSTableView After Updates

I managed to write a function to set the column width by max string length. I need this to prevent long strings from clipping, wrapping etc.
My problem is now, where do I put the function. In other words when do I know when the tableview finished loading or updating? I cant find any available notifications for NSTableView.
here is my func:
func columnWidthByMaxStringLength(forColumn: Int) -> CGFloat {
let table = equationTableView!
var width = 0
for i in 0...table.numberOfRows-1 {
let view = table.view(atColumn: forColumn, row: i, makeIfNecessary: true)
let size = view?.fittingSize
width = max(width, Int((size?.width)!))
}
return CGFloat(width)
}
I tried to call this function within the NSTableView delegate func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? but it is not the right place. It throws an error.
The width of the column can be calculated when the table view and the data are loaded, before updating the table view. You can reuse the cell view.
For example:
override func viewDidLoad() {
super.viewDidLoad()
self.data = …
self.sizeToFit(column: tableView.column(withIdentifier: NSUserInterfaceItemIdentifier("name")))
self.tableView.reloadData()
}
func sizeToFit(column: Int) {
if let view = self.tableView.view(atColumn: column, row: 0, makeIfNecessary: true) as? NSTableCellView {
let key = self.tableView.tableColumns[column].identifier.rawValue
var width = self.tableView.tableColumns[column].minWidth
for object in self.data {
view.textField?.objectValue = object[key]
let size = view.fittingSize
width = max(width, size.width)
}
self.tableView.tableColumns[column].width = width
}
}

Handle Double-click Mouse Event and Return Pressed for NSTableView

OK, what I need is pretty straightforward, though I can still find nothing specific.
I want to be able to :
track double-click events
track when the NSTableView is in focus, and the "Return" key is pressed.
How would you go about it?
P.S. I've had a look into NSTableViewDelegate specification, but I can't find anything useful.
For double click you need to do just these :
-(void)awakeFromNib{
[self.tableView setDoubleAction:#selector(thisMethod)];
//And if you wish to take selector dynamically, I guess you know how to do :)
}
-(void)thisMethod{
NSLog(#"double clicked");
}
For the return event, subclass your NSTableView and override keyDown:
Swift 5.x:
override func keyDown(with event: NSEvent) {
if event.characters?.count == 1 {
let character = event.keyCode
switch (character) {
// 36 is return
case UInt16(36):
print("return: \(event)")
default:
print("any other key: \(event)")
}
} else {
super.keyDown(with: event)
}
}
There is a way to handle the Return key without having to manually check for its key code.
I'll show the answer in Swift, but it can be applied in Objective-C as well.
First, override keyDown(with:) in your view controller subclass that controls the table view and call interpretKeyEvents(_:):
override func keyDown(with event: NSEvent) {
interpretKeyEvents([event])
}
Second, in the same view controller subclass, override insertNewLine(_:). This is called when the user presses the Return key:
override func insertNewLine(_ sender: Any?) {
// Add your logic to handle the Return key being pressed
}
Here's an example:
class TableViewController: NSViewController {
#IBOutlet var tableView: NSTableView!
override func keyDown(with event: NSEvent) {
interpretKeyEvents([event])
}
override func insertNewLine(_ sender: Any?) {
guard tableView.selectedRow >= 0 else { return }
print("Pressed Return on row \(tableView.selectedRow)")
}
}