Autolayout in an NSView being used as a cell for NSTableView - cocoa

I am trying to build an NSTableView similar to how Things does it's layout.
I assume what they are doing is using an NSTableView with a custom-drawn NSTableCell -- or perhaps it's an NSSegmentedControl. I am attempting to go down the NSTableCell route. I attempted to subclass an NSTableCellView and draw a custom cell (this is all in the init method for testing);
- (id)init {
self = [super init];
if (self) {
_checkbox = [[NSButton alloc] init];
[_checkbox setButtonType:NSSwitchButton];
_textview = [[NSTextView alloc] init];
[self addSubview:_checkbox];
[self addSubview:_textview];
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
NSDictionary *views = NSDictionaryOfVariableBindings(_checkbox, _textview);
[self addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_checkbox]-[_textview]|"
options:0
metrics:nil
views:views]];
}
return self;
}
#end
Seems pretty self-explanatory, but it doesn't actually work. I am getting errors on constraints not being able to be satisfied. Is it not possible to use autolayout inside a subclassed NSTableCellView?

It is possible although you have to introduce some additional code to the table view controller:
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
{
NSTableCellView *rowView = [self.tableView makeViewWithIdentifier: RVIssueSelectionTableRowViewContentKey owner: self];
[rowView setObjectValue: yourDataObject]; // or update your cell with the according data the way you prefer
[rowView setNeedsLayout: YES];
[rowView layoutSubtreeIfNeeded];
return [rowView fittingSize].height;
}
This will cause the cell to update it's layout and you can return it's desired height. Because this call can be kind of expensive you should cache the calculated cell height.
This answer has been taken from another SO answer which I could not find right now (will update the solution when I've found it).
Your error messages for the constraints are generated because you force the cell to have a height of x (through the table view datasource methods). But your constraints want to set the height of the cell so they are satisfied. Both at the same time can not work.

Related

Programmatically implementing two different layouts using size classes

I have a four buttons layout. In portrait they should be shown one above the other. In landscape they should be in two columns each with two buttons.
I implement the buttons in code - really simple stuff:
UIButton *btn1 = [[UIButton alloc] init];
[self.view addSubview: btn1];
UIButton *btn2 = [[UIButton alloc] init];
[self.view addSubview: btn2];
UIButton *btn3 = [[UIButton alloc] init];
[self.view addSubview: btn3];
UIButton *btn4 = [[UIButton alloc] init];
[self.view addSubview: btn4];
NSDictionary *views = NSDictionaryOfVariableBindings(btn1, btn2, btn3, btn4);
[btn1 setTranslatesAutoresizingMaskIntoConstraints:NO];
[btn2 setTranslatesAutoresizingMaskIntoConstraints:NO];
[btn3 setTranslatesAutoresizingMaskIntoConstraints:NO];
[btn4 setTranslatesAutoresizingMaskIntoConstraints:NO];
// portrait constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(50)-[btn1]-(50)-|"
options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(50)-[btn2]-(50)-|"
options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(50)-[btn3]-(50)-|"
options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(50)-[btn4]-(50)-|"
options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[btn1]-[btn2]-[btn3]-[btn4]-(50)-|"
options:0 metrics:nil views:views]];
This is obviously the setup for portrait layout. I would used to have determined the device and its orientation to make specific case for iPad and iPhone in there respective orientations. But now we are supposed to use size classes. How can I determine if the size class is "compact"... and thus set the appropriate constraints?
In the meantime I have found a good solution. Since this question has so many upvotes, I thought I would quickly describe it. I was inspired to this solution by a WWDC session.
I have moved on to Swift so please excuse that the code will be in swift - the concept is however the same for Obj-C.
You start out by declaring three constraint arrays:
// Constraints
private var compactConstraints: [NSLayoutConstraint] = []
private var regularConstraints: [NSLayoutConstraint] = []
private var sharedConstraints: [NSLayoutConstraint] = []
And then you fill the constraints accordingly. You can i.e. do this in a separate function that you call from viewDidLoad or you do it in viewDidLoad directly.
sharedConstraints.append(contentsOf: [
btnStackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
...
])
compactConstraints.append(contentsOf: [
btnStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.7),
...
])
regularConstraints.append(contentsOf: [
btnStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.4),
...
])
The important part is switching between the size classes and activating/deactivating the appropriate constraints.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if (!sharedConstraints[0].isActive) {
// activating shared constraints
NSLayoutConstraint.activate(sharedConstraints)
}
if traitCollection.horizontalSizeClass == .compact && traitCollection.verticalSizeClass == .regular {
if regularConstraints.count > 0 && regularConstraints[0].isActive {
NSLayoutConstraint.deactivate(regularConstraints)
}
// activating compact constraints
NSLayoutConstraint.activate(compactConstraints)
} else {
if compactConstraints.count > 0 && compactConstraints[0].isActive {
NSLayoutConstraint.deactivate(compactConstraints)
}
// activating regular constraints
NSLayoutConstraint.activate(regularConstraints)
}
}
I know that the constraints don't fit to the ones in the question. But the constraints themselves are irrelevant. The main thing is how one switches between two sets of constraints based on the size class.
Hope this helps.
You can examine the view's trait collection to determine its horizontal and vertical size class.
if (self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
...
}
Implement the traitCollectionDidChange: method to automatically be called when a trait changes due to autorotation.
For more information, see UITraitCollection Class Reference and UITraitEnvironment Protocol Reference.
Swift 4 code for the accepted answer:
if (self.view.traitCollection.horizontalSizeClass == .compact) {
...
}
traitCollection only works in iOS8. So your app will crash on iOS7. Use the code below to support both iOS7 and iOS8
if ([self.view respondsToSelector:#selector(traitCollection)]){
if (self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
...
}
}

Memory Warning in UICollectionView (with image)

I am currently working on a UICollectionView with a lot of images. However, it sometimes crashes in this view with memory warning. I am using AFNetworking and UIImageView+AFNetworking category to set image through setImageWithURL: method. One issue can be caching. I am not sure if AFNetworking deals with image caching. Anyway, is there a way to optimize this code in terms of memory management? Or if I am to implement didReceiveMemoryWarning method in this view controller, what can be put in this method? I attach the code for cellForItemAtIndexPath for this collection view.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"RecipeCell" forIndexPath:indexPath];
// setting the image view for the cell using AFNetworking. Does this do caching automatically?
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:6];
if (PRODUCTION) {
[recipeImageView setImageWithURL:[[self.recipes objectAtIndex:indexPath.row] objectForKey:#"recipe_image"] placeholderImage:[UIImage imageNamed:#"default_recipe_picture.png"]];
} else {
[recipeImageView setImageWithURL:[NSString stringWithFormat:#"http://localhost:5000/%#", [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"recipe_image"]] placeholderImage:[UIImage imageNamed:#"default_recipe_picture.png"]];
}
// configure the back of the cell. fill all the info.
UITextView *recipeNameView = (UITextView *)[cell viewWithTag:8];
recipeNameView.text = [NSString stringWithFormat:#"%#", [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"recipe_name"]];
UILabel *recipeNameLabel = (UILabel *)[cell viewWithTag:2];
recipeNameLabel.text = [NSString stringWithFormat:#"%#", [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"recipe_name"]];
NSDictionary *user = [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"user"];
UIButton *chefNameButton = (UIButton *)[cell viewWithTag:3];
[chefNameButton setTitle:[NSString stringWithFormat:#"%# %#", [user objectForKey:#"first_name"], [user objectForKey:#"last_name"]] forState:UIControlStateNormal];
NSMutableArray *missingIngredientsStringArray = [[NSMutableArray alloc] init];
NSArray *missingIngredients = [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"missing_ingredients"];
for (NSDictionary *missingIngredient in missingIngredients) {
[missingIngredientsStringArray addObject:[missingIngredient objectForKey:#"name"]];
}
NSString *missingIngredientsString = [missingIngredientsStringArray componentsJoinedByString:#","];
UITextView *missingIngredientsView = (UITextView *)[cell viewWithTag:4];
missingIngredientsView.text = [NSString stringWithFormat:#"%u Missing Ingredients: %#", missingIngredients.count, missingIngredientsString];
// configure the front of the cell. chef name button and missing ingredients and likes on front view
UIButton *frontNameButton = (UIButton *)[cell viewWithTag:11];
[frontNameButton setTitle:[NSString stringWithFormat:#"%# %#", [user objectForKey:#"first_name"], [user objectForKey:#"last_name"]] forState:UIControlStateNormal];
[frontNameButton sizeToFit];
frontNameButton.frame = CGRectMake(160 - [frontNameButton.titleLabel.text sizeWithFont:[UIFont boldSystemFontOfSize:13]].width - 7, frontNameButton.frame.origin.y, frontNameButton.frame.size.width, frontNameButton.frame.size.height);
UILabel *likesLabel = (UILabel *)[cell viewWithTag:9];
likesLabel.text = [NSString stringWithFormat:#"%# likes", [[self.recipes objectAtIndex:indexPath.row] objectForKey:#"likes"]];
UIButton *missingIngredientsButton = (UIButton *)[cell viewWithTag:12];
[missingIngredientsButton setBackgroundImage:[UIImage imageNamed:#"badge_green.png"] forState:UIControlStateSelected];
if (missingIngredients.count == 0) {
missingIngredientsButton.selected = YES;
[missingIngredientsButton setTitle:#"" forState:UIControlStateNormal];
} else {
missingIngredientsButton.selected = NO;
[missingIngredientsButton setTitle:[NSString stringWithFormat:#"%u", missingIngredients.count] forState:UIControlStateNormal];
}
// make back view invisible.
UIView *backView = [cell viewWithTag:1];
UIView *frontView = [cell viewWithTag:5];
frontView.alpha = 1.0;
backView.alpha = 0;
// adding flip gesture recognizers
UIView *flipView1 = [cell viewWithTag:12];
UIView *flipView2 = [cell viewWithTag:1];
UITapGestureRecognizer *flipGestureRecognizer1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(flipCell:)];
UITapGestureRecognizer *flipGestureRecognizer2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(flipCell:)];
[flipView1 addGestureRecognizer:flipGestureRecognizer1];
[flipView2 addGestureRecognizer:flipGestureRecognizer2];
return cell;
}
[Edit] I attach a screenshot of my Instruments run.
You can see that memory allocation increases as I just push segue and press back button repeatedly. Things that just keep increasing are CFData, CALayer, CABackingStore, UITableView. I doubt these are things that are created after segue, and they are not being released... Please help!
You're probably going to want some sort of image caching strategy to avoid re-downloading images. And UIImageView+AFNetworking category does cache images for you. But you may also have the responses being cached in the in-memory URL cache, which in this case is somewhat redundant.
So you might consider reducing or turning off the in-memory URL cache. I had the issue you're describing and the following reduced my memory issues quite a bit:
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
AFNetworking automatically stores images in an NSCache collection, which automatically removes some or all of the images from memory on a low memory warning. AFNetworking is probably not your issue.
In fact, I don't think displaying images is your issue unless you're downloading lots of very large images and displaying them simultaneously. (If this is the case, you should try optimizing your images for display on the device so they don't need to be resized.)
One issue I see is that you are adding a gesture recognizer to the cell every time it comes into the view, but cells are reused, so when a cell comes in again you are adding unnecessary gesture recognizers to it. You could resolve this by subclassing UITableViewCell and assigning the gesture recognizers as properties. You could also resolve this by checking flipView1 and flipView2 to see if they have gesture recognizers attached before adding them. (I'm not sure if this is enough to cause a memory warning though.)
I'd recommend going to Build -> Profile and selecting the Allocations instrument. On the left, select Objective C only, and hide system calls. Then, scroll through your collection view and look at the instrument to see what's taking up all the memory.
UPDATE
Here's a screenshot of the Allocations tool:

How to use autolayout with view-based NSTableView when view are provided by an NSViewController?

I have made the following example app to illustrate my question.
The left view is a place holder view (added in Interface Builder). When the App loads I add a subview managed by a NSViewController. The NSViewController draws the different coloured rectangles, each of which is a NSView, and the layout of these coloured views managed by constraint created programmatically and added to the -loadView method of the controller.
The right view is an NSTableView (added in Interface Builder). When the App loads I use the same NSViewController class to provide views to for the table view (only one row is added).
When I add the subview the the place holder view I also add two additional constraints,
[_placeholderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[CTRL_VIEW]|" options:0 metrics:nil views:views]];
[_placeholderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[CTRL_VIEW]|" options:0 metrics:nil views:views]];
These constraints set the frame of the subview to be equal to the bounds of the superview. All is good.
However, when I provide the view for the NSTableView using the delegate method -tableView:viewForTableColumn:row:, the view has not yet been added to the table. As such it doesn't have a superview, so constraints cannot (yet) be added to the view. This is why the view in the table view does not have the same bounds as the table view cell.
So my question is how can I add constraints to the view I supply to the table view? Can I access the view again after the table view has added it? This seems a bit hack-ish.
The source code for the AppDelegate.h,
#import <Cocoa/Cocoa.h>
#class BlahViewController;
#interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource, NSTableViewDelegate>
#property (assign) IBOutlet NSWindow *window;
/* Left view controller and place holding view */
#property (strong) BlahViewController *viewController;
#property (weak) IBOutlet NSView *placeholderView;
/* Right view (which is an NSTableView) */
#property (weak) IBOutlet NSTableView *tableView;
#end
and AppDelegate.m,
#import "AppDelegate.h"
#import "BlahViewController.h"
#interface AppDelegate ()
#property NSMutableArray *tableData;
#end
#implementation AppDelegate
- (id)init
{
self = [super init];
if (self) {
_tableData = [[NSMutableArray alloc] init];
[_tableData addObject:[[BlahViewController alloc] initWithNibName:#"BlahViewController" bundle:nil]];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints"];
}
return self;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
/* Add a managed subview to the place holder view*/
_placeholderView.layer.backgroundColor = CGColorCreateGenericGray(0.5, 1.0);
_viewController = [[BlahViewController alloc] initWithNibName:#"BlahViewController" bundle:nil];
[_viewController.view setFrame:_placeholderView.bounds];
[_placeholderView addSubview:_viewController.view];
/* Additional constraints so the managed subview resizes with the place holder view */
NSDictionary *views = #{ #"CTRL_VIEW" : _viewController.view };
[_placeholderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[CTRL_VIEW]|" options:0 metrics:nil views:views]];
[_placeholderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[CTRL_VIEW]|" options:0 metrics:nil views:views]];
}
-(NSInteger) numberOfRowsInTableView:(NSTableView *)tableView
{
return [_tableData count];
}
-(id) tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
return [_tableData[row] view];
}
-(CGFloat) tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row {
return 150.;
}
#end
Update #jrturton
Adding the constraints in -(void) tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row worked.
#swalkner
The constraints I added are very basic,
-(void) tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row {
NSView *view = [rowView viewAtColumn:0];
NSDictionary *views = NSDictionaryOfVariableBindings(view);
[view.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-12-[view]-12-|" options:0 metrics:nil views:views]];
[view.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-12-[view]-12-|" options:0 metrics:nil views:views]];
}
You can access the view after it has been added to the table in the delegate method tableView:willDisplayCell:forTableColumn:row:. At this point you can add the constraints, but you might want to be careful not to keep adding the same constraints over and over again.
As you found out yourself, the method tableView:didAddRowView:forRow: would be a better place, as this will only get called once per new view.
You may also be able to achieve this by setting a flexible width autoresizing mask on your view instead of any horizontal size constraints. The table will probably be taking this into account when adding cell views. You can mix and match autoresizing and constraints if you're careful.
I know some iOS, some Autolayout and only a little OS X so I can't give you much more than that. We don't have to worry about cell widths changing much in iOS land!
macOS 10.13 seems to include this capability out of the box with usesAutomaticRowHeights, but it is does not yet have explanation in the Cocoa docs:
https://developer.apple.com/documentation/appkit/nstableview/2870126-usesautomaticrowheights
Also, this is featured in IB as a configuration setting on cell view height.
I haven't been able to verify, but maybe someone can.

Adding NSTableView to NSView Programmatically

I am having a bit of trouble adding a NSTableView to an NSView programatically. The view is the first view of an NSSplitView. My Pointers are set up right i am sure of it because I can add a NSButton to the view no problem. Also my tableview's delegate and datasource methods are working as expected. If I use interface builder to add the table view to my view it works. However, I dont want to use IB. I would like to be able to do this through code. Here is the code I am currently using.
-(void)awakeFromNib{
tableData = [[NSMutableArray alloc]initWithObjects:#"March",#"April",#"May", nil];
tableView = [[NSTableView alloc]initWithFrame:firstView.frame];
[tableView setDataSource:self];
[tableView setDelegate:self];
[firstView addSubview:tableView];
NSButton *j = [[NSButton alloc]initWithFrame:firstView.frame];
[j setTitle:#"help"];
[firstView addSubview:j];
}
The NSButton object appears on screen although if I comment out the button the tableview does not appear. What am I doing wrong. Thanks for the help.
Thank you, from your help I was able to figure this out. IB automatically inserts the NSScrollview around the table view and it also inserts a column for you. In order to do it this from code you need to allocate a scroll view and a column. Here is what I am currently utilizing if anyone else comes across this problem.
-(void)awakeFromNib{
tableData = [[NSMutableArray alloc]initWithObjects:#"March",#"April",#"May", nil];
NSScrollView * tableContainer = [[NSScrollView alloc] initWithFrame:firstView.bounds];
//This allows the view to be resized by the view holding it
[tableContainer setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
tableView = [[NSTableView alloc] initWithFrame:tableContainer.frame];
[tableView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
NSTableColumn *column =[[NSTableColumn alloc]initWithIdentifier:#"1"];
[column.headerCell setTitle:#"Header Title"];
[tableView addTableColumn:column];
[tableView setDataSource:self];
[tableView setDelegate:self];
[tableContainer setDocumentView:tableView];
[firstView addSubview:tableContainer];
//You mentioned that ARC is turned off so You need to release these:
[tableView release];
[tableContainer release];
[column release];
}
Thanks for your help.
NSTableView by default is in NSScrollView. So You can do it like this:
tableData = [[NSMutableArray alloc] initWithObjects:#"March",#"April",#"May", nil];
NSScrollView * tableContainer = [[NSScrollView alloc] initWithFrame:firstView.frame];
tableView = [[NSTableView alloc] initWithFrame:firstView.frame];
[tableView setDataSource:self];
[tableView setDelegate:self];
[tableContainer setDocumentView:tableView];
[firstView addSubview:tableContainer];
//You mentioned that ARC is turned off so You need to release these:
[tableView release];
[tableContainer release];

Custom NSScroller issues

I'm trying to subclass NSScroller in order to draw my own scroller knob. To do this, I've subclassex NSScrollView and usex the following code to instantiate my custom NSScrollers:
- (void)awakeFromNib;
{
NSRect horizontalScrollerFrame = [[self horizontalScroller] frame];
NSRect verticalScrollerFrame = [[self verticalScroller] frame];
NSString *scrollBarVariant = [[[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain] valueForKey:#"AppleScrollBarVariant"];
if (![scrollBarVariant isEqualToString:#"DoubleBoth"]) {
[self setVerticalScroller:[[[TRScroller alloc] initWithFrame:verticalScrollerFrame] autorelease]];
[self setHorizontalScroller:[[[TRScroller alloc] initWithFrame:horizontalScrollerFrame] autorelease]];
}
}
This works and my NSScrollers display correctly. But I'm occasionally seeing rendering issues upon first loading my application. Within Interface Builder I have laid out a number of NSScrollViews with their scrollbars set to hide automatically. The issue I'm seeing is that when the application first loads, the scrollbar backgrounds are rendered across the NSScrollViews contents.
alt text http://www.freeimagehosting.net/uploads/1d3fc75db8.png
I believe this is because I instantiate my NSScroll subclass (TRSubclass) via awakeFromNib, which means that the scrollbars are given the frame of the NSScrollView before it is automatically resized to meet the windows saved location and size (in other words, it's using the frame that's assigned by default within Interface Builder). What's the best way around this?
I've tried forcing the NSScrollView to redisplay (using setNeedsDisplay: and display:) but with no luck. Has anyone else come across a similar issue?
I'm using the same schema in my applications and I fighted this issues a lot. I use the same trick: scrollers are substituted in [scrollView awakeFromNib] methods, but I don't face such rendering issues at the moment. You can try to play with "draws background" property of the NSScrollView - it really helps sometimes
- (void)changeSubs
{
// change clip view
// ...
// change scrollers
NSRect horizontalScrollerFrame = [[self horizontalScroller] frame];
NSRect verticalScrollerFrame = [[self verticalScroller] frame];
if (![[self verticalScroller] isKindOfClass:[CRScroller class]])
[self setVerticalScroller:[[[CRScroller alloc] initWithFrame:verticalScrollerFrame] autorelease]];
if (![[self horizontalScroller] isKindOfClass:[CRScroller class]])
[self setHorizontalScroller:[[[CRScroller alloc] initWithFrame:horizontalScrollerFrame] autorelease]];
}
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self changeSubs];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSKeyedUnarchiver* unpacker = (id)aDecoder;
[unpacker setClass:[CRClipView class] forClassName:[NSClipView className]];
[unpacker setClass:[CRScroller class] forClassName:[NSScroller className]];
self = [super initWithCoder:aDecoder];
if (self)
{
}
return self;
}
- (void)awakeFromNib
{
[self changeSubs];
}
There are few tricks here, they work depending on a way NSScrollView is created. 'isKindOfClass' check helps to avoid double-swap.

Resources