iOS6 UICollectionView and UIPageControl - How to get visible cell? - objective-c

While studying iOS6 new features I got a question about UICollectionView.
I am currently testing it with Flow layout and the scroll direction set to horizontal, scrolling and paging enabled. I've set its size to exactly the same as my custom's cells, so it can show one at a time, and by scrollig it sideways, the user would see the other existing cells.
It works perfectly.
Now I want to add and UIPageControl to the collection view I made, so it can show up which cell is visible and how many cells are there.
Building up the page control was rather simple, frame and numberOfPages defined.
The problem I am having, as the question titles marks, is how to get which cell is currently visible in the collection view, so it can change the currentPage of the page control.
I've tried delegate methods, like cellForItemAtIndexPath, but it is made to load cells, not show them. didEndDisplayingCell triggers when a cell its not displayed anymore, the opposite event of what I need.
Its seems that -visibleCells and -indexPathsForVisibleItems, collection view methods, are the correct choice for me, but I bumped into another problem. When to trigger them?
Thanks in advance, hope I made myself clear enough so you guys can understand me!

You must setup yourself as UIScrollViewDelegate and implement the scrollViewDidEndDecelerating:method like so:
Objective-C
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat pageWidth = self.collectionView.frame.size.width;
self.pageControl.currentPage = self.collectionView.contentOffset.x / pageWidth;
}
Swift
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageWidth = self.collectionView.frame.size.width
pageControl.currentPage = Int(self.collectionView.contentOffset.x / pageWidth)
}

I struggled with this for a while as well, then I was advised to check out the parent classes of UICollectionView. One of them happens to be UIScrollView and if you set yourself up as a UIScrollViewDelegate, you get access to very helpful methods such as scrollViewDidEndDecelerating, a great place to update the UIPageControl.

I would recommend a little tuned calculation and handling as it will update page control immediately in any scroll position with better accuracy.
The solution below works with any scroll view or it subclass (UITableView UICollectionView and others)
in viewDidLoad method write this
scrollView.delegate = self
then use code for your language:
Swift 3
func scrollViewDidScroll(_ scrollView: UIScrollView)
{
let pageWidth = scrollView.frame.width
pageControl.currentPage = Int((scrollView.contentOffset.x + pageWidth / 2) / pageWidth)
}
Swift 2:
func scrollViewDidScroll(scrollView: UIScrollView)
{
let pageWidth = CGRectGetWidth(scrollView.frame)
pageControl.currentPage = Int((scrollView.contentOffset.x + pageWidth / 2) / pageWidth)
}
Objective C
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat pageWidth = self.collectionView.frame.size.width;
self.pageControl.currentPage = (self.collectionView.contentOffset.x + pageWidth / 2) / pageWidth;
}

Another option with less code is to use visible item index path and set the page control.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
self.pageControl.currentPage = [[[self.collectionView indexPathsForVisibleItems] firstObject] row];
}

Place PageControl in your view or set by Code.
Set UIScrollViewDelegate
In Collectionview-> cellForItemAtIndexPath (Method) add the below
code for calculate the Number of pages,
int pages
=floor(ImageCollectionView.contentSize.width/ImageCollectionView.frame.size.width);
[pageControl setNumberOfPages:pages];
Add the ScrollView Delegate method,
pragma mark - UIScrollVewDelegate for UIPageControl
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat pageWidth = ImageCollectionView.frame.size.width;
float currentPage = ImageCollectionView.contentOffset.x / pageWidth;
if (0.0f != fmodf(currentPage, 1.0f))
{
pageControl.currentPage = currentPage + 1;
}
else
{
pageControl.currentPage = currentPage;
}
NSLog(#"finishPage: %ld", (long)pageControl.currentPage);
}

I know this is an old one but I've just needed to implement this sort of feature again and have a bit to add which gives a more complete answer.
Firstly: Using scrollViewDidEndDecelerating assumes that the user lifted their finger while dragging (more like a flick action) and therefore there is a deceleration phase. If the user drags without lifting the finger the UIPageControl will still indicate the old page from before the drag began. Instead using the scrollViewDidScroll callback means that the view is updated both after dragging and flicking and also during dragging and scrolling so it feels much more responsive and accurate for the user.
Secondly: Relying on the pagewidth for calculating the selected index assumes all the cells have the same width and that there is one cell per screen. taking advantage of the indexPathForItemAtPoint method on UICollectionView gives a more resilient result which will work for different layouts and cell sizes. The implementation below assumes the centre of the frame is the desired cell to be represented in the pagecontrol. Also if there are intercell spacings there will times during scrolling when the selectedIndex could be nil or optional so this needs to be checked and unwrapped before setting on the pageControl.
func scrollViewDidScroll(scrollView: UIScrollView) {
let contentOffset = scrollView.contentOffset
let centrePoint = CGPointMake(
contentOffset.x + CGRectGetMidX(scrollView.frame),
contentOffset.y + CGRectGetMidY(scrollView.frame)
)
if let index = self.collectionView.indexPathForItemAtPoint(centrePoint){
self.pageControl.currentPage = index.row
}
}
One more thing - set the number of pages on the UIPageControl with something like this:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
self.pageControl.numberOfPages = 20
return self.pageControl.numberOfPages
}

Simple Swift
public func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
pageControl.currentPage = (collectionView.indexPathsForVisibleItems().first?.row)!
}
UIScrollViewDelegate is already implemented if you implement UICollectionViewDelegate

If using scrollViewDidScroll, updating the page control should be done manually to ⚠️ avoid the flickering dots when you tap on the page control.
Setup the UIPageControl.
let pageControl = UIPageControl()
pageControl.pageIndicatorTintColor = .label
pageControl.defersCurrentPageDisplay = true // Opt-out from automatic display
pageControl.numberOfPages = viewModel.items.count
pageControl.addTarget(self, action: #selector(pageControlValueChanged), for: .valueChanged)
Implement the action (using the extensions below).
#objc func pageControlValueChanged(_ sender: UIPageControl) {
collectionView.scroll(to: sender.currentPage)
}
Update UIPageControl manually on every scroll.
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
pageControl.currentPage = collectionView.currentPage
pageControl.updateCurrentPageDisplay() // Display only here
}
}
Convinient UICollectionView extensions.
extension CGRect {
var middle: CGPoint {
CGPoint(x: midX, y: midY)
}
}
extension UICollectionView {
var visibleArea: CGRect {
CGRect(origin: contentOffset, size: bounds.size)
}
var currentPage: Int {
indexPathForItem(at: visibleArea.middle)?.row ?? 0
}
func scroll(to page: Int) {
scrollToItem(
at: IndexPath(row: page, section: 0),
at: .centeredHorizontally,
animated: true
)
}
}

Related

UIView animation seemingly affecting UICollectionViewCell

I have a UICollectionView (backed by IGListKit), and a UIViewAnimation block that animates some text in a custom navbar (a plain UIView, not a UINavigationBar) when the UICollectionView is scrolled beyond a certain point. The animation however seems to be affect the layout of the UICollectionViewCell - it seems to have the right height set but it's doing a transform animation, see video.
If I remove the animation, the cell behaves just fine.
I'm pretty confused as the two don't seem related at all. Does anyone have any idea what's happening here?
https://i.imgur.com/G6jnfSl.mp4
Animation function for the navbar
func showTitle(_ isShowing: Bool) {
guard isShowingTitle != isShowing else { return }
UIView.animate(withDuration: 0.2) {[weak self] in
guard let self = self else { return }
self.breadcrumb.font = isShowing ? BaseNavBar.subtitleFont : BaseNavBar.titleFont
self.breadcrumb.textColor = isShowing ? Color.fontSecondary : Color.fontPrimary
self.mainTitle.alpha = isShowing ? 1 : 0
self.mainTitle.isHidden = !isShowing
}
isShowingTitle = isShowing
}

How do I avoid the jerking when I do an Infinite Horizontal Scroll?

Platform: iOS 10+
I'm able to make a crude infinite scrolling loop via watching WWDC11.
However I can't figure out the algorithm to smooth out the jerking effect.
Any ideas would be appreciated.
Here's the collection view, to be scrolled horizontally:
Here's the UICollectionView where I'm attempting to create an infinite horizontal scroll effect (allowing for the initial scroll to trigger it):
#implementation PhotoCollectionView
- (void)recenterIfNecessary {
CGPoint currentOffset = self.contentOffset;
CGFloat contentWidth = self.contentSize.width;
CGFloat centerOffsetX = (contentWidth - self.bounds.size.width) / 2.0;
CGFloat distanceFromCenter = fabs(currentOffset.x - centerOffsetX);
if (contentWidth > 0 && (distanceFromCenter > (contentWidth / 2.5))) {
self.contentOffset = CGPointMake(centerOffsetX, currentOffset.y);
// Move content by the same amount so it appears to stay still.
// Note: Need to determine correct algorithm.
// Ran out-of-time on this one.
}
}
- (void)layoutSubviews {
[super layoutSubviews];
[self recenterIfNecessary];
}
#end
How do I provide a SMOOTH scroll vs the cell-replenish jerk?
...I want to also use this within Swift 4+ as well as Objective-C.

Resize cells when bounds UICollectionView change

I'm using a horizontally, paging UICollectionView to display a variable number of collection view cells. The size of each collection view cell needs to be equal to that of the collection view and whenever the size of the collection view changes, the size of the collection view cells need to update accordingly. The latter is causing issues. The size of the collection view cells is not updated when the size of the collection view changes.
Invalidating the layout doesn't seem to do the trick. Subclassing UICollectionViewFlowLayout and overriding shouldInvalidateLayoutForBoundsChange: doesn't work either.
For your information, I'm using an instance of UICollectionViewFlowLayout as the collection view's layout object.
I think solution below is much cleaner. You only need to override one of UICollectionViewLayout's method like:
- (void)invalidateLayoutWithContext:(UICollectionViewFlowLayoutInvalidationContext *)context
{
context.invalidateFlowLayoutAttributes = YES;
context.invalidateFlowLayoutDelegateMetrics = YES;
[super invalidateLayoutWithContext:context];
}
and
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
if(!CGSizeEqualToSize(self.collectionView.bounds.size, newBounds.size))
{
return YES;
}
return NO;
}
as well.
I have similar behavior in my app: UICollectionView with cells that should have the same width as collection view at all time. Just returning true from shouldInvalidateLayoutForBoundsChange: didn't work for me either, but I managed to make it work in this way:
class AdaptableFlowLayout: UICollectionViewFlowLayout {
var previousWidth: CGFloat?
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
let newWidth = newBounds.width
let shouldIvalidate = newWidth != self.previousWidth
if shouldIvalidate {
collectionView?.collectionViewLayout.invalidateLayout()
}
self.previousWidth = newWidth
return false
}
}
In documentation it is stated that when shouldInvalidateLayoutForBoundsChange returns true then invalidateLayoutWithContext: will be called. I don't know why invalidateLayout works and invalidateLayoutWithContext: doesn't.
Swift 4 Xcode 9 implementation for height changes:
final class AdaptableHeightFlowLayout: UICollectionViewFlowLayout {
var previousHeight: CGFloat?
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
let newHeight = newBounds.height
let shouldIvalidate = newHeight != self.previousHeight
if shouldIvalidate {
collectionView?.collectionViewLayout.invalidateLayout()
}
self.previousHeight = newHeight
return false
}
}

Creating a hidden UISegmentedControl in a UITableView

Like the iBooks app, when you pull down the tableview, a search bar and segmented control appear, to allow you to search and switch between two types of views.
It sticks in that position when you pull down far enough, and alternatively, gets hidden when you pull the tableview up enough.
I am trying to implement the same thing with a UISegmentedControl.
So far I have added a segmented control successfully as a subview to the table. (It has a negative Y frame so make it stick above the tableview).
I have also implemented this code:
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
float yOffset = scrollView.contentOffset.y;
if (yOffset < -70) {
[scrollView setContentOffset:CGPointMake(0.0f, -70.0f) animated:YES];
} else if (yOffset > -10) {
[scrollView setContentOffset:CGPointMake(0.0f, -11.0f) animated:YES];
}
}
This works great, until I try using the segmented control. Where the table will just act like it is scrolling, ignoring the segmented control altogether (i.e. if I tap on a segment, it doesn't even get selected, instead the table scrolls up, hiding the segmented control.
I did use the scrollViewDidScroll method but this made it buggy and the scrolling jumpy.
I also tried to make the segmented control's exclusiveTouch = YES, but this had no effect whatsoever.
I would be thankful for all help! thanks in advance!
Here is my code which works:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//
// Table view
//
if ([scrollView isKindOfClass:[myTableView class]]) {
//
// Discover top
//
CGFloat topY = scrollView.contentOffset.y + scrollView.contentInset.top;
if (topY <= self.tableHeaderHeightConstraint.constant) {
[self setIsScrolledToTop:YES];
} else {
[self setIsScrolledToTop:NO];
}
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
//
// Table view
//
if ([scrollView isKindOfClass:[myTableView class]]) {
//
// Toggle favourite category
//
if ([self isScrolledToTop]) {
//
// Show
//
} else {
//
// Hide
//
}
}
}
Edited the above code to make it a bit more generic, but syntactically its correct

Customize right click highlight on view-based NSTableView

I have a view-based NSTableView with a custom NSTableCellView and a custom NSTableRowView. I customized both of those classes because I want to change the appearance of each row. By implementing the [NSTableRowView draw...] methods I can change the background, the selection, the separator and the drag destination highlight.
My question is: how can I change the highlight that appears when the row is right clicked and a menu appears?
For example, this is the norm:
And I want to change the square highlight to a round one, like this:
I'd imagine this would be done in NSTableRowView by calling a method like drawMenuHighlightInRect: or something, but I can't find it. Also, how can the NSTableRowView class be doing this if I customized, in my subclass, all of the drawing methods, and I don't call the superclass? Is this drawn by the table itself?
EDIT:
After some more experimenting I found out that the round highlight can be achieved by setting the tableview as a source list. Nonetheless, I want to know how to customize it if possible.
I know I'm a bit late to offer any help to the OP, but hopefully this can spare some other folks a little bit of time. I subclassed NSTableRowView to achieve the right-click contextual menu highlight (why Apple doesn't have a public drawing method to override this is beyond me). Here it is in all its glory:
BSDSourceListRowView.h
#import <Cocoa/Cocoa.h>
#interface BSDSourceListRowView : NSTableRowView
// This needs to be set when a context menu is shown.
#property (nonatomic, assign, getter = isShowingMenu) BOOL showingMenu;
#end
BSDSourceListRowView.m
#import "BSDSourceListRowView.h"
#implementation BSDSourceListRowView
- (void)drawBackgroundInRect:(NSRect)dirtyRect
{
[super drawBackgroundInRect:dirtyRect];
// Context menu highlight:
if ( self.isShowingMenu ) {
[self drawContextMenuHighlight];
}
}
- (void)drawContextMenuHighlight
{
BOOL selected = self.isSelected;
CGFloat insetY = ( selected ) ? 2.f : 1.f;
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(self.bounds, 2.f, insetY) xRadius:6.f yRadius:6.f];
NSColor *fillColor, *strokeColor;
if ( selected ) {
fillColor = [NSColor clearColor];
strokeColor = [NSColor whiteColor];
} else {
fillColor = [NSColor colorWithCalibratedRed:95.f/255.f green:159.f/255.f blue:1.f alpha:0.12f];
strokeColor = [NSColor alternateSelectedControlColor];
}
[fillColor setFill];
[strokeColor setStroke];
[path setLineWidth:2.f];
[path fill];
[path stroke];
}
- (void)drawSelectionInRect:(NSRect)dirtyRect
{
[super drawSelectionInRect:dirtyRect];
if ( self.isShowingMenu ) {
[self drawContextMenuHighlight];
}
}
- (void)setShowingMenu:(BOOL)showingMenu
{
if ( showingMenu == _showingMenu )
return;
_showingMenu = showingMenu;
[self setNeedsDisplay:YES];
}
#end
Feel free to use any of it, change any of it, or do whatever you want with any of it. Have fun!
Updated for Swift 3.x:
SourceListRowView.swift
import Cocoa
open class SourceListRowView : NSTableRowView {
open var isShowingMenu: Bool = false {
didSet {
if isShowingMenu != oldValue {
needsDisplay = true
}
}
}
override open func drawBackground(in dirtyRect: NSRect) {
super.drawBackground(in: dirtyRect)
if isShowingMenu {
drawContextMenuHighlight()
}
}
override open func drawSelection(in dirtyRect: NSRect) {
super.drawSelection(in: dirtyRect)
if isShowingMenu {
drawContextMenuHighlight()
}
}
private func drawContextMenuHighlight() {
let insetY: CGFloat = isSelected ? 2 : 1
let path = NSBezierPath(roundedRect: bounds.insetBy(dx: 2, dy: insetY), xRadius: 6, yRadius: 6)
let fillColor, strokeColor: NSColor
if isSelected {
fillColor = .clear
strokeColor = .white
} else {
fillColor = NSColor(calibratedRed: 95/255, green: 159/255, blue: 1, alpha: 0.12)
strokeColor = .alternateSelectedControlColor
}
fillColor.setFill()
strokeColor.setStroke()
path.lineWidth = 2
path.fill()
path.stroke()
}
}
Note: I haven't actually run this, but I'm pretty sure this should do the trick in Swift.
Stop Default Drawing
Several answers describe how to draw a custom contextual-click highlight. However, AppKit will continue to draw the default one. There is an easy trick to stop that and I didn't want it to get lost in a comment: subclass NSTableView and override -menuForEvent:
// NSTableView subclass
override func menu(for event: NSEvent) -> NSMenu?
{
// DO NOT call super's implementation.
return self.menu
}
Here, I assume that you've assigned a menu to the tableView in IB or have set the tableView's menu property programatically. NSTableView's default implementation of -menuForEvent: is what draws the contextual menu highlight.
Solve Bad Apple Engineering
Now that we're not calling super's implementation of menuForEvent:, the clickedRow property of our tableView will always be -1 when we right-click, which means our menuItems won't target the correct row of our tableView.
But fear not, we can do Apple Engineering's job for them. On our custom NSTableView subclass, we override the clickedRow property:
class MyTableView: NSTableView
{
private var _clickedRow: Int = -1
override var clickedRow: Int {
get { return _clickedRow }
set { _clickedRow = newValue }
}
}
Now we update the -menuForEvent: method:
// NSTableView subclass
override func menu(for event: NSEvent) -> NSMenu?
{
let location: CGPoint = convert(event.locationInWindow, from: nil)
clickedRow = row(at: location)
return self.menu
}
Great. We solved that problem. Onwards to the next thing:
Tell Your RowView To Do Custom Drawing
As others have suggested, add a custom Bool property to your NSTableRowView subclass. Then, in your drawing code, inspect that value to decide whether to draw your custom contextual highlight. However, the correct place to set that value is in the same NSTableView method:
// NSTableView subclass
override func menu(for event: NSEvent) -> NSMenu?
{
let location: CGPoint = convert(event.locationInWindow, from: nil)
clickedRow = row(at: location)
if clickedRow > 0,
let rowView: MyCustomRowView = rowView(atRow: tableRow, makeIfNecessary: false) as? MyCustomRowView
{
rowView.isContextualMenuTarget = true
}
return self.menu
}
Above, I've created MyCustomRowView (a subclass of NSTableRowView) and have added a custom property: isContextualMenuTarget. That custom property looks like this:
// NSTableRowView subclass
var isContextualMenuTarget: Bool = false {
didSet {
needsDisplay = true
}
}
In my drawing method, I inspect the value of that property and, if it's true, draw my custom highlight.
Clean Up When The Menu Closes
You have a controller that implements the datasource and delegate methods for your tableView. That controller is also likely the delegate for the tableView's menu. (You can assign that in IB or programatically.)
Whatever object is your menu's delegate, implement the menuDidClose: method. Here, I'm working in Objective-C because my controller is still ObjC:
// NSMenuDelegate object
- (void) menuDidClose:(NSMenu *)menu
{
// We use a custom flag on our rowViews to draw our own contextual menu highlight, so we need to reset that.
[_outlineView enumerateAvailableRowViewsUsingBlock:^(__kindof MyCustomRowView * _Nonnull rowView, NSInteger row) {
rowView.isContextualMenuTarget = NO;
}];
}
Performance Note: My tableView will never have more than about 50 entries. If you have a table with THOUSANDS of visible rows, you would be better served to save the rowView that you set isContextualMenuTarget=true on, then access that rowView directly in -menuDidClose: so you don't have to enumerate all rowViews.
Single-Column: This example assumes a single column tableView that has the same NSMenu for each row. You could adapt the same technique for multi-column and/or varying NSMenus per row.
And that's how you beat AppKit in the face until it does what you want.
This is already a bit old, but I've wasted on it quite a bit of time, so posting my solution in case it could help anyone:
In my case, I wanted to remove the lines completely
Lines are not "Focus" rings, they are some stuff Apple is doing in undocument API
The ONLY way I found to remove them (Without using Undocumented API) is by opening NSMenu programmatically, without Interface Builder.
For that, I had to cache "right-click" event on TableViewRow, which has some issue since not always called, so I've dealt with that issue too.
A. Subclass NSTableView:
Overriding right click event, calculating the location of click to get a correct row, and transferring it to my custom NSTableRowView!
class TableView: NSTableView {
override func rightMouseDown(with event: NSEvent) {
let location = event.locationInWindow
let toMyOrigin = self.superview?.convert(location, from: nil)
let rowIndex = self.row(at: toMyOrigin!)
if (rowIndex < 0 || self.numberOfRows < rowIndex) {
return
}
if let isRowExists = self.rowView(atRow: rowIndex, makeIfNecessary: false) {
if let isMyTypeRow = isRowExists as? MyNSTableRowView {
isMyTypeRow.costumRightMouseDown(with: event)
}
}
}
}
B. Subclass MyNSTableRowView
Presenting NSMenu programmatically
class MyNSTableRowView: NSTableRowView {
//My custom selection colors, don't have to implement this if you are ok with the default system highlighted background color
override func drawSelection(in dirtyRect: NSRect) {
if self.selectionHighlightStyle != .none {
let selectionRect = NSInsetRect(self.bounds, 0, 0)
Colors.tabSelectedBackground.setStroke()
Colors.tabSelectedBackground.setFill()
let selectionPath = NSBezierPath.init(roundedRect: selectionRect, xRadius: 0, yRadius: 0)
selectionPath.fill()
selectionPath.stroke()
}
}
func costumRightMouseDown(with event: NSEvent) {
let menu = NSMenu.init(title: "Actions:")
menu.addItem(NSMenuItem.init(title: "Some", action: #selector(foo), keyEquivalent: "a"))
NSMenu.popUpContextMenu(menu, with: event, for: self)
}
#objc func foo() {
}
}
I agree with MCMatan that this is not something you can tweak by changing any draw calls. The box will remain.
I took his approach of bypassing the default menu launch, but left the context menu setup as default in my NSTableView. I think this is a simpler way.
I derive from NSTableView and add the following:
public private(set) var rightClickedRow: Int = -1
override func rightMouseDown(with event: NSEvent)
{
guard let menu = self.menu else { return }
let windowClickLocation = event.locationInWindow
let outlineClickLocation = convert(windowClickLocation, from: nil)
rightClickedRow = row(at: outlineClickLocation)
menu.popUp(positioning: nil, at: outlineClickLocation, in: self)
}
override func rightMouseUp(with event: NSEvent) {
rightClickedRow = -1
}
My rightClickedRow is analogous to clickedRow for the table view. I have an NSViewController that looks after my table, and it is set as the table's menu delegate. I can use rightClickedRow in the delegate calls, such as menuNeedsUpdate().
I'd take a look at the NSTableRowView documentation. It's the class that is responsible for drawing selection and drag feedback in a view-based NSTableView.