Detect cells in NSTableView that become invisible after scrolling - macos

I want to detect when cells in NSTableView become invisible when the user scrolls. In iOS, the UITableView has a delegate method with signature tableView:didEndDisplayingCell:forRowAtIndexPath, but I could not find anything simular in the delegate methods of NSTableView.

Related

UITextfield in a custom uicollectioncell - delegate methods not being called

I have a simple uicollectionviewcell with some buttons and a uitextfield. Tapping the uitextfield pops the keyboard but my Done button makes the keyboard 'blink' and then the uppercase arrows fill in. Weird.
None of my uitextfield methods are called (I have them logged). The uitextfield is in the nib file and I tried setting delegate in code and in IB. Neither work but doing it in IB causes a crash with no info other than ldb.
I also have the Return key set to Done.
Any solutions?
Turns out I was dragging the delegate to the files owner - it needed to be dragged to the cells view. Solved.

Table cell only is highlighted when you tap on the disclosure icon

I place a single table view cell on my view using interface builder and add an accessory type, a selection color and enable user interaction.
My problem is that the cell only is highlighted when the user taps on the arrow icon. But it also should be highlighted when the user taps on the text label inside it, he can tap anywhere on the cell.
How can I achieve this?
A UITableViewCell is really only meant to be used within the context of a UITableView (which knows how to handle and/or delegate the selection & highlighting of the cell).
Having a UITableViewCell outside of the context of a table is a somewhat unexpected user interface.
Couldn't you do what you want to do with a UIButton instead?
Or a UIView that has two UIButton subviews (or at least one UIButton subview to contain a widget graphic that looks like the disclosure accessory)? That way the things you want to get highlighted would definitely happen, as opposed to depending on a nonexistent [UITableView didSelectCellAtIndexPath:] method that doesn't exist in a view that isn't a UITableView delegate.

How to pass scroll events to parent NSScrollView

I need fixed-size NSTextViews inside a larger scrolling window. IB requires that the textviews be inside their own NSScrollViews, even though their min/max sizes are fixed so that they won’t actually scroll. When trackpad gestures are made within the textview frames (regardless of whether they have focus), they are captured by the textviews’ scrollviews, so nothing happens.
How do I tell the textviews’ scrollviews to pass scroll events up to the window’s main scrollview? (Or perhaps I should be asking how I tell the window’s main scrollview to handle these events itself and not pass them on to its child scrollviews.)
The IB structure is like this:
window
window’s content view
big scrollview for window (desired target for scroll events)
box
swappable content view in separate xib
scrollview for textview
textview
And, yes, the window does scroll correctly when the textviews do not have focus.
You needn't create a outlet "svActive" to track your super scrollview. Just write this sentence in scrollWheel event:
[[self nextResponder] scrollWheel:event];
this will pass the event to next responder in the responder chain.
IB does not require you have a text view inside a NSScrollView; this is just the default, because most of the time you'll want your view to scroll. Select the NSTextView and choose Layout > Unembed Objects. Note that after this point, you can no longer move or resize your view in IB. This seems to be a bug.
Here's an example of how to put two NSTextViews in a single NSScrollView.
Add two text views next to each other; put some text in them so you can see what's happening.
Select the views; choose Layout > Embed Objects In > Scroll View. This puts them in a generic NSView inside a NSScrollView.
Select the text views; choose Layout > Unembed Objects.
Turn off the springs and struts (autosizing) for each text view, so they don't resize when you shrink the scroll view.
Take note of the height of the document view (here it's 175).
Make the scroll view smaller. This also resizes the document view (NSView).
Restore the document view to its original size (I set the height back to 175).
Done! Scrolling works as you'd expect.
This is really embarrassing. After weeks of putting it off, I made a first attempt to get a subclassed NSScrollView to behave passively — and it turned out to be a no brainer.
Here’s the subclass:
h file:
#import <Cocoa/Cocoa.h>
#interface ScrollViewPassive : NSScrollView {
// This property is assigned a ref to windowController’s main scrollview.
NSScrollView *svActive;
}
#property (nonatomic, retain) NSScrollView *svActive;
#end
m file:
#import "ScrollViewPassive.h"
#implementation ScrollViewPassive
#synthesize svActive;
// Pass any gesture scrolling up to the main, active scrollview.
- (void)scrollWheel:(NSEvent *)event {
[svActive scrollWheel:event];
}
#end
There’s no need to make outlets for these passive scrollviews; I give them their refs to the main scrollview right after their xibs are assigned as content to the NSBox:
[self.boxDisplayingTextViews setContentView:self.subviewCtllr1.view];
// A textview's superview's superview is its scrollview:
((ScrollViewPassive *)[[self.subviewCtllr1.textview1 superview] superview]).svActive = self.scrollviewMain;
That’s it. Works like a charm.
I find that IB3 and Xcode4 both fight you if you try to do this directly, but you can do it indirectly. First, drag the textview out of the scrollview and delete the scrollview. You'll wind up with an orphaned textview. I don't know any way to get IB to allow you to put this into your window, but it'll be in your NIB. Now, attach an IBOutlet to it, and at runtime do a addSubview: and adjust its frame to move it into whatever scrollview you wanted it to be in.
In my experience, NIBs are a good thing, but every complex NIB I've ever worked with needed some final rearranging in code.
Based on #phaibin's answer, here's how you'd do it in Swift 4.2.
First, subclass NSScrollView and override scrollWheel:
class ScrollThingy: NSScrollView{
override func scrollWheel(with event: NSEvent) {
self.nextResponder?.scrollWheel(with: event)
}
}
Then place the ScrollThingy class (or whatever you name it) on the NSScrollView that is wrapped around your NSTextView. The parent NSScrollView will get the scroll event thereafter.

NSTextView overlay causes oddities with first responder status

I have an NSTextView in an NSScrollView, and I am programmatically inserting an NSView subclass as a subview of the NSTextView. This NSView acts as an overlay, superimposing graphical information about the text beneath it.
I thought it was working quite well until I noticed that the text view does not respond to right clicks. Other operations (editing, selection) seem to work just fine.
Also, if the first responder is changed to a sibling of the scroll view (an outline view, for example) the text view does not regain first responder status from clicking on it. The selection will change in response to clicking, but the selection highlight is gray instead of blue (indicating that the text view is not the first responder).
If I offset the frame of the overlay subview, the text view behaves 100% normally in the area not overlapped by the overlay, but the overlapped area behaves incorrectly, as outlined above.
Steps To Replicate This Behavior on Mac OS X 10.6.4:
Create a plain old non-document-based Cocoa app.
Add an `NSTextView' IBOutlet to the app delegate .h.
Add an NSTextView to the window in MainMenu.xib. Connect the textView outlet.
Type in a bit of code:
In applicationDidFinishLaunching:
NSView *overlay = [[NSView alloc] initWithFrame:textView.bounds];
[textView addSubview:overlay];
[overlay release];
Run the app, observe that right click in the text area does not work as it should, yet you can still otherwise interact with the text view.
Next, add an NSOutlineView to the window in the xib. Observe that once focus leaves the text area (if you click on the outline view) with the overlay in place, you cannot set the focus back to the text view (it will not become first responder again).
Is there some way I can enable the NSTextView to receive all of its events, even though my NSView overlay does not accept first responder or mouse events? I suspect this might be related to the field editor – perhaps it is ignoring events it thinks are destined to the overlay view?
You probably need to make your overlay an instance of a custom view class that forwards all events and accessibility messages to the text view. You may also need to convert any view-relative coordinates to the text view's coordinate system.
I don't have a lot of experience with it, but another possibility would be to use a Core Animation layer as an overlay.
A clean way to handle this is by making your overlay view a custom subclass of NSView, and then overriding the hitTest: method to always return nil. This will prevent the overlay view from participating in the responder chain. Instead, events will get sent automatically to it's superview or views higher up the view hierarchy. You might also want to override acceptsFirstResponder to return NO to be safe (in case it's accidentally set programatically).

NSTableView jumps on selection… why?

I've got an NSTableView with a single column, populated with NSTableViewDataSource methods (no bindings involved.) The NSTableView is inside an NSScrollView, as is the default behavior when you drag an NSTableView in from the library in Interface Builder. The contents of the tableview are populated based upon a search string that the user types, and are not changed after that point.
I am only implementing these two methods of the DataSource protocol:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView;
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
After typing my search string, the focus is still in the NSTextField, but I can naturally scroll the tableview with my mouse's scroll wheel or with the arrows on the scroll bar. Neither causes the tableview to receive the focus ring. However, if I scroll down such that I am not at the top of the tableview and then click to highlight an individual row in the tableview, the tableview instead jumps back to the top, and then selects whatever row is under my mouse at that point. If I click on rows after that point, it works as expected.
In other words, if the NSTableView has the focus ring around it, clicking on a row highlights the expected row. If it does not have the focus ring around it, clicking on a row selects whatever row is at that position after scrolling to the top.
Any insight? I am running Snow Leopard, but I believe it happened when I was running Leopard as well.
Could it be that when the search NSTextField loses focus, it somehow fires a reloadData (or similar method) on the table view, which would empty out the view (resetting its scrolling position to the top) and then re-populating it instantaneously?
I had a similar bug once, and the cause was that my window controller was handling awakeFromNib and was setting the scroll position on the list so it would be in the right position when first loaded. However, I eventually realised that it was also getting awakeFromNib messages from the nib-based table view cells which are created as needed, which would sometimes mess with the table scroll value unexpectedly while scrolling. My fix was to make the window controller only set scroll position when handling the first awakeFromNib.

Resources