NSTableView: get notified when floating subview gets removed - macos

I have a custom NSTableView with floating group rows. These rows behave differently withing drawRect, according to whenever they are actually floating, or not. (There is only 1 floating row in an NSTableView at a time, that's the table's default behaviur)
I can track when they start floating, with a custom NSScrollView subclass, with obseverable override for the addFloatingSubview:forAxis: method.
I can't track when they stop floating though.
The NSScrollView documentation tells me:
You are responsible for keeping track of the floating views and removing them via removeFromSuperview when they should no longer float.
But it seems NSTableView does not call removeFromSuperview neither on (my custom subclass of) NSTableRowView; nor on the NSView used as a "cell" for the group row.
How to get notified when the NSTableView stops floating a subview?

I've found it: NSTableRowView.floating
NSTableView has so many subcomponents, I just did not seek for this on the right/trivial place :)

Related

NSTableView in NSScrollView doesn't autoscroll when dragging

I'm currently implementing drag and drop rearranging in a table view in my OS X app. While normal scrolling works fine, autoscroll while dragging it totally broken.
If I grab a cell and start dragging, autoscroll just tells the table to scroll to the top. If I manually scroll using the trackpad during dragging the table continually pops to the top. If I drag one of the top cells, the table will not autoscroll down when dragging near the bottom.
I subclassed NSScrollView and overrode the scrollClipView method. I see that it's being called by some internal autoscroll method with the coordinates of (0, 0).
Since I can't see what that internal method is doing, and Goggle and SO are turning up nothing, I'm a bit stuck.
Has anyone run into this issue before? From past experiences, I have the feeling it's something AutoLayout related, but I have no idea what. Or maybe it's something completely unrelated.
Any ideas on how to further troubleshoot?
I ran into the same issue. In my case, the problem was that I set the height of the NSTableCellView to 10,000 in Interface Builder so that the horizontal separators wouldn’t be displayed for empty rows below the actual rows.
However, the actual height of my NSTableCellViews loaded at run time was 43px.
So as soon as I started dragging a cell to re-order it, the NSScrollView was trying to scroll 10,000 pixels at a time instead of 43 at a time.
This seems like a bug, because my NSOutlineView subclass does implement the following method to dynamically set the height of each row.
func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat
Apparently that method is ignored by the autoscroll mechanism during drag and drop operations, and only the value set in Interface Builder is used.
So I just changed the height of the dummy NSTableCellView to 43px in Interface Builder, and I’ll just live with the horizontal separators being displayed for empty rows.

NSTableBackgroundView

I have a ViewController which has a single TableView within its content.
Within the ViewController I access the actual Table via:
NSView * myView = self.view.subviews[0];
myView = myView.subviews[0];
NSTableView *myTable = myView.subviews[0];
[self.targets sortUsingDescriptors: [myTable sortDescriptors]];
[myTable reloadData];
This actually is clumsy but seems to work. Except that when I mouse down within the table when the vertical scroll bars are active. Then, I raise an exception because in the final line of accessing subviews to reach myTable, the subview array has several NSTableBackgroundViews before the actual TableView that I created and want to access.
NSTableBackgroundView apparently is not documented and does not have a sortDescriptors method (hence the exception).
Perhaps someone can point out a better way to get to the table which is more reliable, or can someone say whether enumerating the final subview array to find the desired ClassOf NSTableView is safe?
Many Thanks!
in lieu of any other answers, I implemented planB, I took the array of subviews and enumerated on them to find the actual NSTableView. This seems to work when I bang on the UI to test it and no more flags are raised.
I wish there were a cleaner way I could discover to get the view I want without traversing all the subviews.

Pinning a NSTableColumn so that it does not scroll horizontally?

I'm trying to figure out a way to have a NSTableColumn be pinned to the left side of my NSTableView. What I have been thinking of is putting two NSTableView's side by side, where the first one contains the 'pinned' column and the second on contains the dynamic data. Setting the first table to disable horizontal scrolling, then detecting when either table is scrolled vertically and keeping the two in sync.
What I'm wondering, is if anyone else has any experience in doing something similar? I don't want to re-invent the wheel if its already been done.
Thats exactly what I did. Two NSTableView's directly beside each other. The first I subclassed the NSScrollView and modified the scroll events to do nothing. The second I subclassed again the NSScrollView and modified the scroll events to cause the header to mirror it. Works great.

How to "stick" a UIScrollView subview to top/bottom when scrolling?

You see this in iPhone apps like Gilt. The user scrolls a view, and a subview apparently "sticks" to one edges as the rest of the scrollView slides underneath. That is, there is a text box (or whatever) in the scrollView, that as the scrollView hits the top of the view, then "sticks" there as the rest of the view continues to slide.
So, there are several issues. First, one can determine via "scrollViewDidScroll:" (during normal scrolling) when the view of interest is passing (or re-appearing). There is a fair amount of granularity here - the differences between delegate calls can be a hundred of points or more. That said, when you see the view approach the top of the scrollView, you turn on a second copy of the view statically displayed under the scrollView top. I have not coded this, but it seems like it will lack a real "stick" look - the view will first disappear then reappear.
Second, if one does a setContentOffset:animated, one does not get the delegate messages (Gilt does not do this). So, how do you get the callbacks in this case? Do you use KVO on "scroll.layer.presentationLayer.bounds" ?
Well, I found one way to do this. When the user scrolls by flicking and dragging, the UIScrollView gives its delegate a "scrollViewDidScroll:" message. You can look then to see if the scroller has moved the content to where you need to take some action.
When "sticking" the view, remove it from the scrollView, and then add it to the scrollView's superview (with an origin of 0,0). When unsticking, do the converse.
If you use the UIScrollView setContentOffset:animated:, it gets trickier. What I did was to subclass UIScrollView, use a flag to specify it was setContentOffset moving the offset, then start a fast running timer to monitor contentOffset.
I put the method that handles the math and sticking/unsticking the child view into this subclass. It looks pretty good.
Gilt uses a table view to accomplish this. Specifically, in the table view's delegate, these two methods:
– tableView:viewForHeaderInSection:
and – tableView:heightForHeaderInSection:

Validating a drag to an NSCollectionView isn't reflected visually

I have an NSCollectionView that I want to accept items dragged from elsewhere in my application.
I implement collectionView:validateDrop:proposedIndex:dropOperation: and collectionView:acceptDrop:index:dropOperation: in the collectionview's delegate and register for the appropriate dragged types. Both methods get called fine when I drag the appropriate types, but I don't get a blue focus ring over the collectionview indicating a valid drag.
Have tried both the collection view and its containing scroll view on Default and External settings for the focus ring. Both are just the standard non-derived Cocoa classes. Wondered if there was anything else I should try. Surely it isn't necessary to subclass NSCollectionView for this?
Thanks
Chris
Focus rings are not typically the correct way to provide feedback about drag destinations. Every view does it slightly differently. NSTextView shows the insertion bar. NSTableView shows a blue line in between rows for Before drop operations, and shows a bezel around the row for On drop operations. (See NSTableViewDropOperation)
NSCollectionView shows a "gap" between existing subviews to show where the items will be dropped for Before drop operations, and it will set the selected property on NSCollectionViewItem to YES for On drop operations. (Note: NSCollectionViewItem doesn't do anything by default to visibly represent the selected property. You must implement that yourself.)
Since NSCollectionView's feedback uses existing subviews only, it appears there isn't any feedback at all for empty NSCollectionView's. You would need to subclass to provide this behavior yourself. You could also file a bug to request that NSCollectionView do this itself.

Resources