Programmatically collapse a group row in NSOutlineView - swift2

I have an NSOutlineView with an action (see code) that collapse a row when the user clicks anywhere on that row. However it is not working for group.
Some rows are defined as group via the "shouldShowOutlineCellForItem" delegate method.
I can expand a group row programmatically, but not collapse it. Any suggestions?
isExpanded is correctly set via the notifications.
#IBAction func didClick(sender: AnyObject?)
{
assert(self.root != nil)
let selectedRow = outlineView.clickedRow
let proposedItem = (selectedRow == -1) ? self.root! : outlineView.itemAtRow(selectedRow) as! thOutlineNode
if proposedItem.isExpanded
{
self.outlineView.collapseItem(proposedItem)
}
else
{
self.outlineView.expandItem(proposedItem)
}
}

Possibly duplicate. Based on this existing SO question covering Objective-C, try adding the NSOutlineViewDelegate delegate method
func outlineView(_ outlineView: NSOutlineView, shouldShowOutlineCellForItem item: AnyObject) -> Bool {
return true
}
to the view controller of your NSOutlineView. From the Apple documentation for the NSOutlineViewDelegate, we see that this is expected behaviour:
optional func outlineView(_ outlineView: NSOutlineView,
shouldShowOutlineCellForItem item: AnyObject) -> Bool
...
Discussion
Returning NO causes frameOfOutlineCellAtRow: to return NSZeroRect,
hiding the cell. In addition, the row will not be collapsible by
keyboard shortcuts.

Related

collectionView(_:didDeselectItemsAt:) is never called

For some reason, my NSCollectionView (which uses a custom layout that I wrote) calls its delegate's collectionView(_:didSelectItemsAt:) function when I click an item, but not that same delegate's collectionView(_:didDeselectItemsAt:) when I click off it.
Here's my basic setup:
class MyCollectionViewContainer: NSViewController {
fileprivate lazy var collectionView: NSCollectionView = {
let collectionView = NSCollectionView()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.collectionViewLayout = self.customLayout
collectionView.isSelectable = true
collectionView.allowsEmptySelection = true
collectionView.allowsMultipleSelection = false
}()
}
extension MyCollectionViewContainer: NSCollectionViewDelegate {
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
print("Selected", indexPaths)
// Mutate data to reflect that selection
}
func collectionView(_ collectionView: NSCollectionView, didDeselectItemsAt indexPaths: Set<IndexPath>) {
print("Deselected", indexPaths)
}
}
"Deselected" is never printed... :/
So it turns out my layout wasn't adding any supplementary views of type NSCollectionElementKindInterItemGapIndicator; apparently this is the view that the collection view uses to detect when you click off an item. My quick solution was to add one huge one to my layout that spans the entire collection view, and make its Z index below that of all other items and supplementary views.
We need to set NSCollectionView item selected:
collectionview.isSelectable = true

How to populate a NSTableView on a button click in Swift

I have got a CellBased NSTableView. I need to populate it on Button Click. I have written the code for populating the table implementing NSTableViewDataSource protocol. I have used an Array as Data Source.
The code is as below
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
let numberOfRows:Int = dataArray.count
return numberOfRows
}
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {
}
But everything is getting loaded when I start the application. I want the data to get loaded on a Button Click like an IBACtion
#IBAction func findButton(sender: AnyObject) {
}
Please help!
Don't set the delegate via interface builder, or elsewhere, set it in your button:
#IBAction func findButton(sender: AnyObject) {
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.reloadData()
}
Try setting your UITableView.dataSource to nil. Then when you are ready (button click) call tableView.dataSource = self. You might also have to call tableView.reloadData().
Haven't tested this but should in theory work.

How do i dismiss the keyboard on return key from a UITextView

How do I dismiss the keyboard by pressing the return key on device? I know how to dismiss keyboard, but not when using the UITextView:
resignFirstResponder
I have tried this, but it does not work:
self.messageTextView.delegate = self
And with this function:
func messageTextViewShouldReturn(textView: UITextView) -> Bool
{
self.messageTextView.resignFirstResponder()
return true
}
Remember to add the UITextDelegate into ViewDidLoad()
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if text == "\n"
{
textView.resignFirstResponder()
return false
}
return true
}
Swift 5
unlike a textField where IBActions are available, there are no actions available for text views.
create your textViewOutlet
If you are using a tableview:
identify where your textView row is located:
let textViewIndex = IndexPath(row: 0, section: 1)
then in (_:didSelectRowAt:) dismiss the keyboard if any row other than your textView
if indexPath != textViewIndex {
textViewOutlet.resignFirstResponder()
}
if you are not using a tableview: In your SB viewcontroller, drag a UITapGestureRecognizer to your view. Then create an IBAction from that Tap Gesture Recognizer by control dragging it to your view. In the action dismiss your keyboard from your textViewOutlet:
#IBAction func tapGestureRecognizer(_ sender: Any) {
notesField.resignFirstResponder()
}

collect result of NStableView with checkboxes in Swift

I have hard time trying to collect the number of checkboxes checked inside the second column of a NStableView.
I composed a NSTableView with 2 column (via IB),
the first is named : BugColumn (it contains textfiled)
the second is named : CheckedColumn (it contains checkboxes)
Here is the code used to display strings in the first column :
var objets: NSMutableArray! = NSMutableArray()
...
extension MasterViewController: NSTableViewDataSource
{
func numberOfRowsInTableView(aTableView: NSTableView) -> Int
{
return self.objets.count
}
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView?
{
var cellView: NSTableCellView = tableView.makeViewWithIdentifier(tableColumn!.identifier, owner: self) as! NSTableCellView
if tableColumn!.identifier == "BugColumn"
{
cellView.textField!.stringValue = self.objets.objectAtIndex(row) as! String
}
return cellView
}
The second column is made of checkboxes appearing for each element of the first column.
I would like to know what is the corresponding text in the first column for each checkboxes enabled (checked).
I red a few exemples about NSTableView but, or they do differents things, or they are in Objective-C.
Could someone explain how to do that using swift?
Thanks

NSOutlineView crash when isGroupItem delegate method is used with Swift

I want to deploy Source List using NSOutlineView in a Swift project.
The view controller below works well when the isGroupItem delegate method is not invoked. However, many __NSMallocBlock__ items will be returned when the isGroupItem method is used. Which I have no idea where these items come from. The items I provided are only strings.
class ViewController: NSViewController, NSOutlineViewDataSource, NSOutlineViewDelegate {
let topLevel = ["1", "2"]
let secLevel = ["1": ["1.1", "1.2"], "2": ["2.1", "2.2"]]
func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int {
if let str = item as? String {
let arr = secLevel[str]! as [String]
return arr.count
} else {
return topLevel.count
}
}
func outlineView(outlineView: NSOutlineView, isItemExpandable item: AnyObject) -> Bool {
return outlineView.parentForItem(item) == nil
}
func outlineView(outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject {
var output: String!
if let str = item as? String {
output = secLevel[str]![index]
} else {
output = topLevel[index]
}
return NSString(string: output)
}
func outlineView(outlineView: NSOutlineView, objectValueForTableColumn tableColumn: NSTableColumn?, byItem item: AnyObject?) -> AnyObject? {
return item
}
func outlineView(outlineView: NSOutlineView, isGroupItem item: AnyObject) -> Bool {
return (outlineView.parentForItem(item) == nil)
}
func outlineView(outlineView: NSOutlineView, viewForTableColumn tableColumn: NSTableColumn?, item: AnyObject) -> NSView? {
return outlineView.makeViewWithIdentifier("HeaderCell", owner: self) as NSTextField
}
}
The sample project can be downloaded here
If you check out the NSOutlineView documentation you will see that it stores only pointers; it doesn't retain the objects returned from the child:ofItem: delegate method. So, when you do this line:
return NSString(string: output)
You are returning a new NSString instance that is quickly released (since the outline view does not retain it). After that point, anytime you ask questions about the items you will get a crash, because the NSString has been freed.
The solution is simple: store the NSStrings in an array and return those same instances each time.
corbin
This question has been answered by Ken Thomases in apple developer forum. Here extracted what he said:
The items you provide to the outline view must be persistent. Also, you have to return the same item each time for a given parent and index. You can't return objects that were created ad hoc, like you're doing in -outlineView:child:ofItem: where you call the NSString convenience constructor.
It works fine after persisting the datasource objects as follow:
let topLevel = [NSString(string: "1"), NSString(string: "2")]
let secLevel = ["1": [NSString(string: "1.1"), NSString(string: "1.2")], "2": [NSString(string: "2.1"), NSString(string: "2.2")]]
then return the stored NSString in the outlineView:child:ofItem: datasource method.
It's because NSOutlineView works with objects inherited from NSObject, and Swift string is incompatible type.

Resources