NSOutlineView Source List without a NIB - cocoa

I am trying to figure out how to do a source list 100% in code. I've been hacking away at SideBar demo and various SO questions/answers and have gotten close, but not quite there. I've copied my code below, this seems to work in that it does build a view, with 3 empty cells I can select. What I can't quite figure out is how to get my text to show up in those cells. I will ultimately replace that with my own view, but I'd settle for the stock rendering to work for now..
In my controller:
#implementation SourceListController {
NSScrollView *_scrollView;
NSOutlineView *_sourceList;
SourceListDataSource *_sourceListDataSource;
}
- (void)loadView {
_scrollView = [[NSScrollView alloc] init];
_sourceList = [[NSOutlineView alloc] init];
_sourceListDataSource = [[SourceListDataSource alloc] init];
[_sourceList setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
NSTableColumn *c = [[NSTableColumn alloc] initWithIdentifier: #"Column"];
[c setEditable: NO];
[c setMinWidth: 150.0];
[_sourceList addTableColumn: c];
[_sourceList setOutlineTableColumn:c];
[_sourceList setDelegate:_sourceListDataSource];
[_sourceList setDataSource:_sourceListDataSource];
[_scrollView setDocumentView:_sourceList];
[_scrollView setHasVerticalScroller:YES];
[_sourceList reloadData];
NSLog(_sourceList.dataSource == _sourceListDataSource ? #"YES" : #"NO");
self.view = _scrollView;
}
And in my data source/delegate:
#implementation SourceListDataSource {
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
return item == nil ? 3 : 0;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
return [NSObject new];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
return item == nil;
}
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
NSLog(#"Here %f", tableColumn.width);
NSTableCellView *result = [[NSTableCellView alloc] init];
NSTextField *textField = [[NSTextField alloc] init];
[textField setStringValue:#"Test"];
[result addSubview:textField];
return result;
}
UPDATE: Further digging tells me viewForTableColumn doesn't even get called.. I am stumped as to why..
UPDATE 2: Figured out that it was the missing column and outlineTableColumn leading to not calling the delegate method. I fixed that and updated the code, but still no text shows up.

Your delegate method needs changes, it won't work that way, and also doesn't make use of the new caching methods for views. Mostly, use the NSTableCellView.textFieldproperty instead of adding your own NSTextFieldwithout frame and frame constraints. So, the code that should work looks like this:
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
NSTableCellView *result = [outline makeViewWithIdentifier:#"MyView" owner:self];
if (result == nil) {
// Create the new NSTextField with a frame of the {0,0} with the width of the table.
// Note that the height of the frame is not really relevant, because the row height will modify the height.
CGRect myRect = CGRectMake(0,0,outlineView.framesize.width,0);
result = [[NSTableCellView alloc] initWithFrame:myRect];
// The identifier of the view instance is set to MyView.
// This allows the cell to be reused.
result.identifier = #"MyView";
}
result.textField.stringValue = #"Test";
return result;
}
All the above typed in SO.

Related

NSOutlineView get flipped in 10.9 when click on it

I have a cell based NSOutlineView in that I have sub classed the NSTextfieldCell and I had written following code.
-(void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
// A single cell instance is used to render every row, so we can't store the
// custom view here
NSRect bgFrame = cellFrame;
bgFrame.size.width = bgFrame.size.width + bgFrame.origin.x + 3;
bgFrame.origin.x = 0;
if ([self isHighlighted])
{
[[NSColor blackColor] set];
NSRectFill(bgFrame);
}
else
{
NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor whiteColor] endingColor:[NSColor lightGrayColor]];
[gradient drawInRect:bgFrame angle:90];
}
[controller showViews:controlView frame:bgFrame highlight:[self isHighlighted] && !isEditing];
}
In View controller
-(void)showViews:(NSView*)parent frame:(NSRect)cellFrame highlight:(BOOL)highlight
{
NSView* nestedView;
nestedView = menu100;
[nestedView setFrame: cellFrame];
if ([nestedView superview] != parent)
{
[nestedView prepareContentInRect:[nestedView visibleRect]];
[parent addSubview: nestedView];
}
}
nestedView = menu100; menu100 is a `NSView` with `NSOutlineview`
menu100 view
In NSOutlineView
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
if ([item isKindOfClass:[NSDictionary class]]) {
[cell setBackgroundColor:[NSColor keyboardFocusIndicatorColor]];
}
else
{
if (item!=nil) {
CustomCellController* cellCtrl = [cellViewControllers objectForKey:item];
if (!cellCtrl)
{
cellCtrl = [[CustomCellController alloc] initWithMenu:item];
[cellViewControllers setObject:cellCtrl forKey:item];
}
CustomCell* mycell = cell;
[mycell setController:cellCtrl];
}
}
}
now the view is look like this
Now when I click on child outline view it become flipped.
The above code is working in 10.8 but it is get flipped in 10.9. I am become mad on this. Can any one give suggestion to get rid of this in 10.9.

NSOutlineView (Source List) EXC_BAD_ACCESS error with ARC

I've an ARC enabled project and within IB I've created a window that holds the source list component which I believe is just a configured NSOutlineView. I'm using the magical delegate method:
- (id)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
for which I cannot find any documentation for at all. Once this method is implemented the root node in my outline view will appear, upon which my entire model gets deallocated. Then when I try and expand the root node the app immediately crashes as model no longer exists.
If I don't use this method, my model remains, the source list works but none of the cells appear (understandably). I'm really not doing any thing fancy here at all.
I've never run into this sort of issue with ARC before, but it's late so there is a chance I've done something dumb and just can't see it. Here's the full code:
#implementation RLListController
- (void)awakeFromNib
{
RLPerson *stan = [[RLPerson alloc] initWithName:#"Stan"];
RLPerson *eric = [[RLPerson alloc] initWithName:#"Eric"];
RLPerson *ken = [[RLPerson alloc] initWithName:#"Ken"];
RLPerson *andrew = [[RLPerson alloc] initWithName:#"Andrew"];
RLPerson *daniel = [[RLPerson alloc] initWithName:#"Daniel"];
RLPerson *aksel = [[RLPerson alloc] initWithName:#"Aksel"];
[stan addChild:eric];
[stan addChild:ken];
[stan addChild:andrew];
[ken addChild:daniel];
[daniel addChild:aksel];
self.people = [#[stan] mutableCopy];
}
#pragma mark - Source List dataSource
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
RLPerson *person = item;
return (item != nil) ? [person.children count] : [self.people count];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
RLPerson *person = item;
return (item != nil) ? [person.children count] > 0 : YES;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
RLPerson *person = item;
return (item != nil) ? [person.children objectAtIndex:index] : [self.people objectAtIndex:index];
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
RLPerson *person = item;
return person.name;
}
- (id)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
RLPerson *person = item;
NSTableCellView *cell = [outlineView makeViewWithIdentifier:#"DataCell" owner:self];
cell.objectValue = person;
[cell.textField setStringValue:person.name];
return cell;
}
#end
#implementation RLPerson
- (id)initWithName:(NSString *)name
{
self = [super init];
if(self)
{
_name = [name copy];
_children = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
- (void)addChild:(RLPerson *)child
{
[_children addObject:child];
}
- (void)dealloc
{
NSLog(#"dealloc");
}
#end
I've just figured out a similar crash in my code. I'll describe what the cause was for me... I'm pretty sure the same applies here, but I haven't tested your code.
Be aware that awakeFromNib can be called multiple times if you have multiple NIBs. I believe this is the case if you have NSTableCellView objects embedded within the NSOutlineView within your XIB file, which are extracted when you call makeViewWithIdentifier:owner: within outlineView:viewForTableColumn:item:.
Because you are creating your model objects (stan etc) within awakeFromNib, they are being recreated during these multiple calls. With each call, ARC is cleaning up the previous model objects, but NSOutlineView is still referencing them, hence the later crash when NSOutlineView tries to ask them for more information.
The fix is to move the model object creation out of awakeFromNib, perhaps into an init method instead.
Update:
Some other small points... it also took me a while to find the documentation for the magic outlineView:viewForTableColumn:item: method. For some reason, it is part of the NSOutlineViewDelegate protocol, not NSOutlineViewDataSource. I believe that if you implement this method, you don't need an implementation of outlineView:objectValueForTableColumn:byItem:.

Simple UICollectionView to show images behaves odd: Some Images are displayed correct, some at wrong position, some missing at all

I want to show images in a grid on iPhone using a UICollectionView, showing 3 images a row. For a "dead simple test" (as I thought), I've added 15 JPG images to my project, so that they'll be in my bundle and I can load them simply via [UIImage imageNamed:...].
I think I've done everything correct (setting up & registering UICollectionViewCell subclass, use of UICollectionViewDataSource Protocol methods), however, the UICollectionView behaves very weird:
It shows only a few of the images in the following pattern:
First line shows image 1 & 3, second line is blank, next line like the first again (image 1 & 3 showing properly), fourth line blank, and so on...
If I push a button in my NavBar that triggers [self.collectionView reloadData], random cells appear or disappear. What drives me nuts is that it's not only an issue of images appear or not. Sometime, images also swap between the cells, i.e. they appear for a indexPath they are definitely not wired up!
Here is my code for the cell:
#interface AlbumCoverCell : UICollectionViewCell
#property (nonatomic, retain) IBOutlet UIImageView *imageView;
#end
#implementation AlbumCoverCell
#synthesize imageView = _imageView;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_imageView = [[UIImageView alloc] initWithFrame:frame];
[self.contentView addSubview:_imageView];
}
return self;
}
- (void)dealloc
{
[_imageView release];
[super dealloc];
}
- (void)prepareForReuse
{
[super prepareForReuse];
self.imageView.image = nil;
}
#end
Part of the code for my UICollectionViewController subclass, where 'imageNames' is an NSArray holding all jpg filenames:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.collectionView registerClass:[AlbumCoverCell class] forCellWithReuseIdentifier:kAlbumCellID];
}
#pragma mark - UICollectionViewDataSource Protocol methods
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [self.imageNames count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
AlbumCoverCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kAlbumCellID forIndexPath:indexPath];
NSString *imageName = [self.imageNames objectAtIndex:indexPath.row];
NSLog(#"CV setting image for row %d from file in bundle with name '%#'", indexPath.row, imageName);
cell.imageView.image = [UIImage imageNamed:imageName];
return cell;
}
#pragma mark - UICollectionViewDelegateFlowLayout Protocol methods
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
{
return CGSizeMake(100, 100);
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
{
return UIEdgeInsetsMake(0, 0, 0, 0);
}
From the NSLog statement in cellForItemAtIndexPath: I can see that the method is called for all of the cells (not only the one's displayed) and that the mapping between indexPath.row and filename is correct.
Has anybody an idea what could cause this weird behavior?
In the meantime, I've found the solution. It was actually a very subtle error in the implementation of my UICollectionViewCell subclass AlbumCoverCell.
The problem is that I've set the frame of the cell instance as the frame of the UIImageView subview instead of passing the bounds property of the cell's contentView!
Here is the fix:
#implementation AlbumCoverCell
#synthesize imageView = _imageView;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// WRONG:
// _imageView = [[UIImageView alloc] initWithFrame:frame];
// RIGHT:
_imageView = [[UIImageView alloc] initWithFrame:self.contentView.bounds];
[self.contentView addSubview:_imageView];
}
return self;
}
- (void)prepareForReuse
{
[super prepareForReuse];
// reset image property of imageView for reuse
self.imageView.image = nil;
// update frame position of subviews
self.imageView.frame = self.contentView.bounds;
}
...
#end

Cocoa : Custom TableView cell?

I am not really familiar with tables, as I usually make games, but now I want to create a level builder where I need a table view with custom cells. I have created a nib file and I have subclassed NSTableCellView, but I don't know what to do next. All I have is:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
NSScrollView * tableContainer = [[NSScrollView alloc] initWithFrame:NSMakeRect(self.window.frame.size.width-TABLEWIDTH, 0, TABLEWIDTH, self.window.frame.size.height)];
SpriteTable *sT = [[SpriteTable alloc]initWithFrame:NSMakeRect(self.window.frame.size.width-TABLEWIDTH, 0, TABLEWIDTH, self.window.frame.size.height)];
NSTableView *tableView = [[NSTableView alloc] initWithFrame: sT.bounds];
NSTableColumn* firstColumn = [[[NSTableColumn alloc] initWithIdentifier:#"firstColumn"] autorelease];
[[firstColumn headerCell] setStringValue:#"First Column"];
[tableView addTableColumn:firstColumn];
tableView.dataSource = self;
tableView.delegate = self;
[tableContainer setDocumentView:tableView];
tableContainer.autoresizingMask = NSViewHeightSizable | NSViewMinXMargin;
[self.window.contentView addSubview: tableContainer];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return 4;
}
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
// get an existing cell with the MyView identifier if it exists
CustomCell *result = [tableView makeViewWithIdentifier:#"MyView" owner:self];
// There is no existing cell to reuse so we will create a new one
if (result == nil) {
NSLog(#"result = nil");
// create the new NSTextField with a frame of the {0,0} with the width of the table
// note that the height of the frame is not really relevant, the row-height will modify the height
// the new text field is then returned as an autoreleased object
//result = [[[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 70)] autorelease];
// the identifier of the NSTextField instance is set to MyView. This
// allows it to be re-used
result.identifier = #"MyView";
}
// result is now guaranteed to be valid, either as a re-used cell
// or as a new cell, so set the stringValue of the cell to the
// nameArray value at row
result.imageView.image = [NSImage imageNamed:NSImageNameHomeTemplate];
// return the result.
return result;
}
If any, which delegate methods do I have to implement ? And how do I customize my cell WITH a nib file ?
Do this in ur subview->
#implementation suhasView
#synthesize name,containerView;// container view contains ur subview
- (NSView*) myView
{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSNib *theNib = [[NSNib alloc] initWithNibNamed:#"suhas"bundle:bundle];
[theNib instantiateNibWithOwner:self topLevelObjects:nil];
return containerView;
}
In Controller->
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
suhas *a=[[suhas alloc]initWithFrame:NSMakeRect(0,0, 40, 40)];
NSView * v = [a myView];
[a.name setStringValue:#"suhas"];
return v;
}...//sorry for my naming of the class:)

Make selected row bold in NSTableView

I try to make bold row selection style in NSTableView without any highlighting
I switched highlighting off:
[myTable setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone];
However I have some troubles making text bold in selected row. As I suggested, I need to change properties of NSTableView's source:
- (void) tableViewSelectionDidChange: (NSNotification *) notification
{
NSDictionary *boldFont = [NSDictionary dictionaryWithObject:[NSFont boldSystemFontOfSize:13.0]
forKey:NSFontAttributeName];
NSDictionary *normalFont = [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:13.0]
forKey:NSFontAttributeName];
for(MyClass *obj in tableSourceList)
obj.name = [[NSMutableAttributedString alloc]
initWithString:obj.name
attributes: normalFont];
long row = [myTable selectedRow];
MyClass objectAtSelectedRow = [tableSourceList objectAtIndex: row];
objectAtSelectedRow.name = [[NSMutableAttributedString alloc]
initWithString:dr.dreamname
attributes: boldFont];
[tableSourceList replaceObjectAtIndex:row withObject:objectAtSelectedRow];
}
Unfortunately, there's no effect.
How to make text in row bold when selected?
You can try to achieve by altering the table cell to be rendered in the table's delegate:
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex {
// Maybe a test on the table column is recommanded if some cells should not be modified
NSIndexSet *selection = [aTableView selectedRowIndexes];
if ([selection containsIndex:rowIndex]) {
[aCell setFont:[NSFont boldSystemFontOfSize:12]];
} else {
[aCell setFont:[NSFont systemFontOfSize:12]];
}
}

Resources