Disable scrolling in NSTableView - objective-c

Is there a simple way to disable scrolling of an NSTableView.
It seems there isn't any property on
[myTableView enclosingScrollView] or [[myTableView enclosingScrollView] contentView] to disable it.

This works for me: subclass NSScrollView, setup and override via:
- (id)initWithFrame:(NSRect)frameRect; // in case you generate the scroll view manually
- (void)awakeFromNib; // in case you generate the scroll view via IB
- (void)hideScrollers; // programmatically hide the scrollers, so it works all the time
- (void)scrollWheel:(NSEvent *)theEvent; // disable scrolling
#interface MyScrollView : NSScrollView
#end
#import "MyScrollView.h"
#implementation MyScrollView
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
if (self) {
[self hideScrollers];
}
return self;
}
- (void)awakeFromNib
{
[self hideScrollers];
}
- (void)hideScrollers
{
// Hide the scrollers. You may want to do this if you're syncing the scrolling
// this NSScrollView with another one.
[self setHasHorizontalScroller:NO];
[self setHasVerticalScroller:NO];
}
- (void)scrollWheel:(NSEvent *)theEvent
{
// Do nothing: disable scrolling altogether
}
#end
I hope this helps.

Here's the best solution in my opinion:
Swift 5
import Cocoa
#IBDesignable
#objc(BCLDisablableScrollView)
public class DisablableScrollView: NSScrollView {
#IBInspectable
#objc(enabled)
public var isEnabled: Bool = true
public override func scrollWheel(with event: NSEvent) {
if isEnabled {
super.scrollWheel(with: event)
}
else {
nextResponder?.scrollWheel(with: event)
}
}
}
Simply replace any NSScrollView with DisablableScrollView (or BCLDisablableScrollView if you still use ObjC) and you're done. Simply set isEnabled in code or in IB and it will work as expected.
The main advantage that this has is for nested scroll views; disabling children without sending the event to the next responder will also effectively disable parents while the cursor is over the disabled child.
Here are all advantages of this approach listed out:
✅ Disables scrolling
✅ Does so programmatically, behaving normally by default
✅ Does not interrupt scrolling a parent view
✅ Interface Builder integration
✅ Drop-in replacement for NSScrollView
✅ Swift and Objective-C Compatible

Thanks to #titusmagnus for the answer, but I made one modification so as not to break scrolling when when the "disabled" scrollView is nested within another scrollView: You can't scroll the outer scrollView while the cursor is within the bounds of the inner scrollView. If you do this...
- (void)scrollWheel:(NSEvent *)theEvent
{
[self.nextResponder scrollWheel:theEvent];
// Do nothing: disable scrolling altogether
}
...then the "disabled" scrollView will pass the scroll event up to the outer scrollView and its scrolling will not get stuck down inside its subviews.

Works for me:
- (void)scrollWheel:(NSEvent *)theEvent
{
[super scrollWheel:theEvent];
if ([theEvent deltaY] != 0)
{
[[self nextResponder] scrollWheel:theEvent];
}
}

There is no simple direct way (meaning, there's no property like UITableView's scrollEnabled that you can set), but i found this answer helpful in the past.
One other thing you could try (not sure about this) is subclassing NSTableView and override -scrollWheel and -swipeWithEvent so they do nothing. Hope this helps

Related

Tell When a UIPageViewController is Scrolling (for Parallax Scrolling of an Image)

I am trying to make an effect similar to that found in the new Yahoo weather app. Basically, each page in the UIPageViewController has a background image, and when scrolling through the page view, the Image's location only scrolls about half the speed. How would I do that? I thought I could use some sort of Delegate Method in the UIPageViewController to get the current offset and then update the images like that. The only problem is that I cannot find anyway to tell if the UIPageViewController is being scrolled! Is there a method for that? Thanks!
for (UIView *view in self.pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
[(UIScrollView *)view setDelegate:self];
}
}
this gives you access to all standard scroll view API methods.
And this is not using private Apple API's.
I added traversing through subviews, to 100% find the UIPageViewController's inner scroll view
WARNING:
Be careful with scrollview.contentOffset. It resets as the controller scrolls to new pages
If you need persision scrollview offset tracking and stuff like that, it would be better to use a UICollectionViewController with cells sized as the collection view itself and paging enabled.
I would do this:
Objective-C
for (UIView *v in self.pageViewController.view.subviews) {
if ([v isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)v).delegate = self;
}
}
and implement this protocol
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
Swift
for view in self.pageViewController.view.subviews {
if let scrollView = view as? UIScrollView {
scrollView.delegate = self
}
}
and implement this protocol
func scrollViewDidScroll(scrollView: UIScrollView)
My guess is that it is not a UIPageViewController, but rather a paged UIScrollView. The UIScrollView does give you a constantly repeated delegate method that tracks what is happening as the scrolling takes place.
Alternatively, you might be able to access the paged UIScrollView that the UIPageViewController is secretly using, but you might break something, and I'm not sure how Apple would feel about it.
Use #Paul's snippet -
for (UIView *v in self.pageViewController.view.subviews) {
if ([v isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)v).delegate = self;
}
}
to implement this protocol : -(void)scrollViewDidScroll:(UIScrollView *)scrollView
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGPoint point = scrollView.contentOffset;
float percentComplete;
percentComplete = fabs(point.x - self.view.frame.size.width)/self.view.frame.size.width;
NSLog(#"percentComplete: %f", percentComplete);
}
This gives you the percentage completion of the scroll. Happy coding!
In Swift 3 you could write it even shorter:
if let scrollView = self.pageViewController.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView {
scrollView.delegate = self
}
extension UIPageViewController {
var scrollView: UIScrollView? {
return view.subviews.filter { $0 is UIScrollView }.first as? UIScrollView
}
}
Using:
pageController.scrollView?.delegate = self
What you are looking for is called parallax scrolling, you can find several libraries that can help you with that.
Edit: Matt is right this is not an answer, only a hint. Anyway let's complete it:
For animating a background image that lay behind your UIPageViewController you should use the delegate methods that it offer:
-[id<UIPageViewControllerDelegate> pageViewController:willTransitionToViewControllers:]
-[id<UIPageViewControllerDelegate> pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:]
With these two methods you can calculate the percentage of the scrolling (you should store your controllers in your array to know at which controller you scrolled to and get the percentage)
You are not supposed to change the delegate of the page view controller's scroll view: it can break its normal behaviour and/or not be supported later on.
Instead, you can:
Add a pan gesture to the page view controller's view:
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panRecognized(gesture:)))
view.addGestureRecognizer(panGesture)
panGesture.delegate = self
Add the new function in order to know how the view is being scrolled.
#objc func panRecognized(gesture: UIPanGestureRecognizer) {
// Do whatever you need with the gesture.translation(in: view)
}
Declare your ViewController as UIGestureRecognizerDelegate.
Implement this function:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}

How to detect that UIScrollView is scrolling or that is is dragging

I need to know on a continuous basis when my UIScrollView is scrolling or dragging.
Implement these two delegate methods..
- (void)scrollViewDidScroll:(UIScrollView *)sender{
//executes when you scroll the scrollView
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// execute when you drag the scrollView
}
Better use isTracking to detect if the user initiated a touch-drag.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.isTracking {
// ...
}
}
This answer is deprecated. See #ober's isTracking solution instead
- (void)scrollViewDidScroll:(UIScrollView *)sender{
if(sender.isDragging) {
//is dragging
}
else {
//is just scrolling
}
}

Hide UISearchBar Cancel Button

I have a UISearchDisplayController and UISearchBar hooked up to my ViewController via Outlets from my nib.
I'd like to hide the cancel button so that the user never sees it. The problem is that the following code hides the button, but only after displaying it to the user for a millisecond (e.g., it flashes on the simulator and device and then disappears out of view).
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
controller.searchBar.showsCancelButton = NO;
}
Is there a better way to hide it?
I managed to hide the "Cancel" button by subclassing UISearchBar and override this method:
-(void)layoutSubviews{
[super layoutSubviews];
[self setShowsCancelButton:NO animated:NO];
}
I had the same issue, but fixed it a different way.
For those who can't or don't want to subclass UISearchDisplayController, I fixed the issue by adding a listener on UIKeyboardWillShowNotification, and setting [self setShowsCancelButton:NO animated:NO] there.
In viewWillAppear::
// Add keyboard observer:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillAppear:)
name:UIKeyboardWillShowNotification
object:nil];
Then you create:
- (void)keyboardWillAppear:(NSNotification *)notification
{
[YOUR-SEARCHBAR-HERE setShowsCancelButton:NO animated:NO];
}
Don't forget to add,
[[NSNotificationCenter defaultCenter] removeObserver:self];
in viewWillDisappear:!
Hope this helps!
Similar to Nimrod's answer, you can also subclass UISearchDisplayController and implement the setActive:animated: method:
- (void)setActive:(BOOL)visible animated:(BOOL)animated {
[super setActive:visible animated:animated];
self.searchBar.showsCancelButton = NO;
}
This seems to be a bug within Xcode. I submitted this error to Apple's bug reporting site, and they've followed up asking for more sample code and use-cases.
Thanks everyone for your attempt at solving this problem.
class CustomSearchBar: UISearchBar {
override func setShowsCancelButton(showsCancelButton: Bool, animated: Bool) {
super.setShowsCancelButton(false, animated: false)
}
}
class CustomSearchController: UISearchController, UISearchBarDelegate {
lazy var _searchBar: CustomSearchBar = {
[unowned self] in
let customSearchBar = CustomSearchBar(frame: CGRectZero)
customSearchBar.delegate = self
return customSearchBar
}()
override var searchBar: UISearchBar {
get {
return _searchBar
}
}
}
Had this problem when using the UISearchBar with UISearchController. I'm using my own cancel button, as the cancel button wasn't showing on iPad with showsCancelButton = YES, now it won't hide on iPhone with showsCancelButton = NO!
The following worked for me.
Set the delegate, and initial value:
- (void)viewDidLoad
{
// ...
self.searchController.searchBar.showsCancelButton = NO;
self.searchController.searchBar.delegate = self;
}
Reset showsCancelButton to NO 0.1s after the text bar begins editing.
#pragma mark - UISearchBarDelegate
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
self.searchController.searchBar.showsCancelButton = NO;
});
}
If you want to avoid the subclassing, implement
searchController.searchBar.showsCancelButton = false;
in these two delegate methods (Do not forget to assign delegates):
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
- (void)didPresentSearchController:(UISearchController *)searchController
The first one is called everytime you update the searchBar (Cancel button is visible by default) and the second one is for the first searchBar activation.
Just based on issues I've had before have you tried setting it in:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
I don't know how to ask this question in your question sorry if this is out of place.
If the cancel button shows up when editing the search field of the search bar you could do the following; subclass the search bar and have it implement the UITextFieldDelegateprotocol:
#interface CustomAlignedSearchBar : UISearchBar<UITextFieldDelegate>
Then implement textFieldDidBeginEditing: and do something like:
- (void)textFieldDidBeginEditing:(UITextField *)textField{
[self setShowsCancelButton:self.cancelButtonShown animated:NO];
}
This will make sure that the cancel button will not show up.
After UISearchDisplayController deprecated in iOS8, Apple give handle search presentation to UISearchControllerDelegate.
so you can override searchBar to hide the Cancel button, like below :
- (void)didPresentSearchController:(UISearchController *)searchController {
[searchController.searchBar setShowsCancelButton:NO];
}
if you need hidden Cancel button from inactive state, you need set searchBar on init :
search = [[UISearchController alloc] initWithSearchResultsController:nil];
[search.searchBar setShowsCancelButton:NO];
On iOS 13.0 and later, UISearchController has this property you can use:
#property (nonatomic) BOOL automaticallyShowsCancelButton API_AVAILABLE(ios(13.0)); // Default YES

UIWebview remove tap to focus

A UIWebView is normally focused on the selected content when I double tap a part of a website, and now I want the UIWebView to ignore the double tap but still be able to be interacted with.
How would I go about this?
The Double Tap is recognized by a UITapGestureRecognizer.
Go through the view hierarchy if you really want this.
A little bit tricky, but it should work.
The codes look like this:
- (void)goThroughSubViewFrom:(UIView *)view {
for (UIView *v in [view subviews])
{
if (v != view)
{
[self goThroughSubViewFrom:v];
}
}
for (UIGestureRecognizer *reco in [view gestureRecognizers])
{
if ([reco isKindOfClass:[UITapGestureRecognizer class]])
{
if ([(UITapGestureRecognizer *)reco numberOfTapsRequired] == 2)
{
[view removeGestureRecognizer:reco];
}
}
}
}
I've put a demo here: NoDoubleTapWebView
Hope it helps.
iOS 5.0+:
A UIWebView contains a UIScrollView to display its content and is accessible by the scrollView property of UIWebView. So, one possible way of doing this is to subclass UIScrollView and override -touchesShouldBegin:withEvent:inContentView. You can get the number of taps from a UITouch object from its tapCount property, which can be used to filter out double-tap gestures and call the super method otherwise. This would look something like:
-(BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view {
UITouch * touch = [touches anyObject]; //Get a touch object
if (2 == touch.tapCount) { //If this is a double-tap...
return NO; //...don't begin touches
}
else { //Otherwise, call the superclass' implementation
return YES;
}
}
However, in order to set the web view's scroll view as your custom subclass, you may also have to subclass UIWebView (which is generally frowned upon). With that said, it would likely work to subclass UIWebView and redefine the scrollView property. In the interface file:
#interface MyAwesomeWebView : UIWebView {
}
#property (nonatomic, readonly, retain) MyAwesomeScrollView * scrollView;
#end
And in the implementation file:
#implementation
#dynamic scrollView
#end
Still, proceed with caution.
Pre iOS 5.0:
As Alan Moore wrote in the comments, you can add a transparent view on top of the web view, capture all touch events with hit testing, and forward the touch events to the web view unless there's a double tap.
EDIT: Updated answer to include caveats about subclassing UIWebView, etc.
EDIT 2: Included second answer for pre iOS 5.0
I think the way to do it is to add a UITapGestureRecognizer on top of your UIWebView, then you can filter double taps as you wish.
You can find sample code/discussion here:
Add a UITapGestureRecognizer to a UIWebView

Selection Highlight in NSCollectionView

I have a working NSCollectionView with one minor, but critical, exception. Getting and highlighting the selected item within the collection.
I've had all this working prior to Snow Leopard, but something appears to have changed and I can't quite place my finger on it, so I took my NSCollectionView right back to a basic test and followed Apple's documentation for creating an NSCollectionView here:
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/CollectionViews/Introduction/Introduction.html
The collection view works fine following the quick start guide. However, this guide doesn't discuss selection other than "There are such features as incorporating image views, setting objects as selectable or not selectable and changing colors if they are selected".
Using this as an example I went to the next step of binding the Array Controller to the NSCollectionView with the controller key selectionIndexes, thinking that this would bind any selection I make between the NSCollectionView and the array controller and thus firing off a KVO notification. I also set the NSCollectionView to be selectable in IB.
There appears to be no selection delegate for NSCollectionView and unlike most Cocoa UI views, there appears to be no default selected highlight.
So my problem really comes down to a related issue, but two distinct questions.
How do I capture a selection of an item?
How do I show a highlight of an item?
NSCollectionView's programming guides seem to be few and far between and most searches via Google appear to pull up pre-Snow Leopard implementations, or use the view in a separate XIB file.
For the latter (separate XIB file for the view), I don't see why this should be a pre-requisite otherwise I would have suspected that Apple would not have included the view in the same bundle as the collection view item.
I know this is going to be a "can't see the wood for the trees" issue - so I'm prepared for the "doh!" moment.
As usual, any and all help much appreciated.
Update 1
OK, so I figured finding the selected item(s), but have yet to figure the highlighting. For the interested on figuring the selected items (assuming you are following the Apple guide):
In the controller (in my test case the App Delegate) I added the following:
In awakeFromNib
[personArrayController addObserver:self
forKeyPath:#"selectionIndexes"
options:NSKeyValueObservingOptionNew
context:nil];
New Method
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if([keyPath isEqualTo:#"selectionIndexes"])
{
if([[personArrayController selectedObjects] count] > 0)
{
if ([[personArrayController selectedObjects] count] == 1)
{
personModel * pm = (PersonModel *)
[[personArrayController selectedObjects] objectAtIndex:0];
NSLog(#"Only 1 selected: %#", [pm name]);
}
else
{
// More than one selected - iterate if need be
}
}
}
Don't forget to dealloc for non-GC
-(void)dealloc
{
[personArrayController removeObserver:self
forKeyPath:#"selectionIndexes"];
[super dealloc];
}
Still searching for the highlight resolution...
Update 2
Took Macatomy's advice but still had an issue. Posting the relevant class methods to see where I've gone wrong.
MyView.h
#import <Cocoa/Cocoa.h>
#interface MyView : NSView {
BOOL selected;
}
#property (readwrite) BOOL selected;
#end
MyView.m
#import "MyView.h"
#implementation MyView
#synthesize selected;
-(id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
-(void)drawRect:(NSRect)dirtyRect
{
NSRect outerFrame = NSMakeRect(0, 0, 143, 104);
NSRect selectedFrame = NSInsetRect(outerFrame, 2, 2);
if (selected)
[[NSColor yellowColor] set];
else
[[NSColor redColor] set];
[NSBezierPath strokeRect:selectedFrame];
}
#end
MyCollectionViewItem.h
#import <Cocoa/Cocoa.h>
#class MyView;
#interface MyCollectionViewItem : NSCollectionViewItem {
}
#end
"MyCollectionViewItem.m*
#import "MyCollectionViewItem.h"
#import "MyView.h"
#implementation MyCollectionViewItem
-(void)setSelected:(BOOL)flag
{
[(MyView *)[self view] setSelected:flag];
[(MyView *)[self view] setNeedsDisplay:YES];
}
#end
If a different background color will suffice as a highlight, you could simply use an NSBox as the root item for you collection item view.
Fill the NSBox with the highlight color of your choice.
Set the NSBox to Custom so the fill will work.
Set the NSBox to transparent.
Bind the transparency attribute of the NSBox to the selected attribute of File Owner(Collection Item)
Set the value transformer for the transparent binding to NSNegateBoolean.
I tried to attach Interface builder screenshots but I was rejected bcos I'm a newbie :-(
Its not too hard to do. Make sure "Selection" is enabled for the NSCollectionView in Interface Builder. Then in the NSView subclass that you are using for your prototype view, declare a property called "selected" :
#property (readwrite) BOOL selected;
UPDATED CODE HERE: (added super call)
Subclass NSCollectionViewItem and override -setSelected:
- (void)setSelected:(BOOL)flag
{
[super setSelected:flag];
[(PrototypeView*)[self view] setSelected:flag];
[(PrototypeView*)[self view] setNeedsDisplay:YES];
}
Then you need to add code in your prototype view's drawRect: method to draw the highlight:
- (void)drawRect:(NSRect)dirtyRect
{
if (selected) {
[[NSColor blueColor] set];
NSRectFill([self bounds]);
}
}
That just simply fills the view in blue when its selected, but that can be customized to draw the highlight any way you want. I've used this in my own apps and it works great.
You can also go another way, if you're not subclassing NSView for your protoype view.
In your subclassed NSCollectionViewItem override setSelected:
- (void)setSelected:(BOOL)selected
{
[super setSelected:selected];
if (selected)
self.view.layer.backgroundColor = [NSColor redColor].CGColor;
else
self.view.layer.backgroundColor = [NSColor clearColor].CGColor;
}
And of course, as said by all the wise people before me, make sure "Selection" is enabled for the NSCollectionView in Interface Builder.
In your NSCollectionViewItem subclass, override isSelected and change background color of the layer. Test in macOS 10.14 and Swift 4.2
class Cell: NSCollectionViewItem {
override func loadView() {
self.view = NSView()
self.view.wantsLayer = true
}
override var isSelected: Bool {
didSet {
self.view.layer?.backgroundColor = isSelected ? NSColor.gray.cgColor : NSColor.clear.cgColor
}
}
}
Since none of the existing answers worked super well for me, here is my take on it. Change the subclass of the CollectionView item to SelectableCollectionViewItem. Here is it's code. Comes with a bindable textColor property for hooking your text label textColor binding to.
#implementation SelectableCollectionViewItem
+ (NSSet *)keyPathsForValuesAffectingTextColor
{
return [NSSet setWithObjects:#"selected", nil];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.wantsLayer = YES;
}
- (void) viewDidAppear
{
// seems the inital selection state is not done by Apple in a KVO compliant manner, update background color manually
[self updateBackgroundColorForSelectionState:self.isSelected];
}
- (void)updateBackgroundColorForSelectionState:(BOOL)flag
{
if (flag)
{
self.view.layer.backgroundColor = [[NSColor alternateSelectedControlColor] CGColor];
}
else
{
self.view.layer.backgroundColor = [[NSColor clearColor] CGColor];
}
}
- (void)setSelected:(BOOL)flag
{
[super setSelected:flag];
[self updateBackgroundColorForSelectionState:flag];
}
- (NSColor*) textColor
{
return self.selected ? [NSColor whiteColor] : [NSColor textColor];
}
In my case I wanted an image(check mark) to indicate selection of object. Drag an ImageWell to the Collection Item nib. Set the desired image and mark it as hidden. Go to bindings inspector and bind hidden attribute to Collection View Item.
(In my case I had created a separate nib for CollectionViewItem, so its binded to File's owner. If this is not the case and Item view is in the same nib as the CollectionView then bind to Collection View Item)
Set model key path as selected and Value transformer to NSNegateBoolean. Thats it now whenever the individual cells/items are selected the image will be visible, hence indicating the selection.
Adding to Alter's answer.
To set NSBox as root item. Simply create a new IB document(say CollectionItem) and drag an NSBox to the empty area. Now add all the elements as required inside the box. Now click on File's Owner and set Custom Class as NSCollectionViewItem.
And in the nib where NSCollectionView is added change the nib name for CollectionViewItem
In the NSBox, bind the remaining elements to Files Owner. For a label it would be similar to :
Now to get the highlight color as Alter mentioned in his answer, set desired color combination in the Fill Color option, set the NSBox to transparent and bind the transparency attribute as below:
Now when Collection View Items are selected you should be able to see the fill color of the box.
This was awesome, thanks alot! i was struggling with this!
To clarify for to others:
[(PrototypeView*)[self view] setSelected:flag];
[(PrototypeView*)[self view] setNeedsDisplay:YES];
Replace PrototypeView* with the name of your prototype class name.
In case you are digging around for the updated Swift solution, see this response.
class MyViewItem: NSCollectionViewItem {
override var isSelected: Bool {
didSet {
self.view.layer?.backgroundColor = (isSelected ? NSColor.blue.cgColor : NSColor.clear.cgColor)
}
}
etc...
}
Here is the complete Swift NSCollectionViewItem with selection. Don't forget to set the NSCollectioView to selectable in IB or programmatically.
Tested under macOS Mojave (10.14) and High Sierra (10.13.6).
import Cocoa
class CollectionViewItem: NSCollectionViewItem {
private var selectionColor : CGColor {
let selectionColor : NSColor = (isSelected ? .alternateSelectedControlColor : .clear)
return selectionColor.cgColor
}
override var isSelected: Bool {
didSet {
super.isSelected = isSelected
updateSelection()
// Do other stuff if needed
}
}
override func viewDidLoad() {
super.viewDidLoad()
view.wantsLayer = true
updateSelection()
}
override func prepareForReuse() {
super.prepareForReuse()
updateSelection()
}
private func updateSelection() {
view.layer?.backgroundColor = self.selectionColor
}
}