NSOutlineView Changing disclosure Image - objective-c

I my outline view, i am adding Custom cell, To drawing custom cell, i am referring example code , present in the Cocoa documentation
http://www.martinkahr.com/2007/05/04/nscell-image-and-text-sample/
I want to change the disclosure image of the cell with my custom image, i have tried following things
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
if([item isKindOfClass:[NSValue class]])
{
MyData *pDt = (MyData *)[item pointerValue];
if(pDt->isGroupElement())
{
[cell setImage:pGroupImage];
}
}
}
but that too not working, Is there any other way to change the disclosure image,
also how can i find out in willDisplayCell whether Item is expand or collapse, so i can set the image accordingly,
Is this only the place to change the disclosure image ?

After researching this issue myself, and trying some of the answers here, I have found that the other approaches mentioned will work, but will require that you perform a lot more manual intervention in order to avoid screen artifacts and other strange behavior.
The simplest solution I found is the following, which should work in most cases.
This solution has the added benefit of the system automatically handling a great many other cases, such as column movement, etc, without your involvement.
- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell
forTableColumn:(NSTableColumn *)tableColumn
item:(id)item
{
[cell setImage:[NSImage imageNamed: #"Navigation right 16x16 vWhite_tx"]];
[cell setAlternateImage:[NSImage imageNamed: #"Navigation down 16x16 vWhite_tx"]];
}
In your case, you would wrap this up with your class detection logic, and set the cell images appropriately for your cases.

A nice way to change the disclosure image is to use a view based outline view:
In your ViewController with NSOutlineViewDelegate:
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
CustomNSTableCellView *cell = [outlineView makeViewWithIdentifier:tableColumn.identifier owner:self];
cell.item = item;
return cell;
}
You have to subclass your NSOutlineView and overide the method:
- (id)makeViewWithIdentifier:(NSString *)identifier owner:(id)owner
{
id view = [super makeViewWithIdentifier:identifier owner:owner];
if ([identifier isEqualToString:NSOutlineViewDisclosureButtonKey])
{
// Do your customization
// return disclosure button view
[view setImage:[NSImage imageNamed:#"Disclosure_Categories_Plus"]];
[view setAlternateImage:[NSImage imageNamed:#"Disclosure_Categories_Minus"]];
[view setBordered:NO];
[view setTitle:#""];
return view;
}
return view;
}
//Frame of the disclosure view
- (NSRect)frameOfOutlineCellAtRow:(NSInteger)row
{
NSRect frame = NSMakeRect(4, (row * 22), 19, 19);
return frame;
}

For who looks for Swift2 Solution. Subclass NSRow of your outlineview and override didAddSubview method as below.
override func didAddSubview(subview: NSView) {
super.didAddSubview(subview)
if let sv = subview as? NSButton {
sv.image = NSImage(named:"IconNameForCollapsedState")
sv.alternateImage = NSImage(named:"IconNameForExpandedState")
}
}

You've got the basic idea but what you will need to do is draw the image yourself. Here's the code I use:
- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
NSString *theImageName;
NSInteger theCellValue = [cell integerValue];
if (theCellValue==1) {
theImageName = #"PMOutlineCellOn";
} else if (theCellValue==0) {
theImageName = #"PMOutlineCellOff";
} else {
theImageName = #"PMOutlineCellMixed";
}
NSImage *theImage = [NSImage imageNamed: theImageName];
NSRect theFrame = [outlineView frameOfOutlineCellAtRow:[outlineView rowForItem: item]];
theFrame.origin.y = theFrame.origin.y +17;
// adjust theFrame here to position your image
[theImage compositeToPoint: theFrame.origin operation:NSCompositeSourceOver];
[cell setImagePosition: NSNoImage];
}
You will need 3 different images as you can see, one for the ON state, one for the OFF state and also one for the MIXED state which should be halfway between the two. The mixed state makes sure you still get the opening and closing animation.

This is what i have tried and working so far,
/* because we are showing our own disclose and expand button */
- (NSRect)frameOfOutlineCellAtRow:(NSInteger)row {
return NSZeroRect;
}
- (NSRect)frameOfCellAtColumn:(NSInteger)column row:(NSInteger)row {
NSRect superFrame = [super frameOfCellAtColumn:column row:row];
if ((column == 0) && ([self isGroupItem:[self itemAtRow:row]])) {
return NSMakeRect(0, superFrame.origin.y, [self bounds].size.width, superFrame.size.height);
}
return superFrame;
}
I have subclassed NSOutlineView class and override these methods,
[self isGroupItem] is to check whether its group or not.
but got one problem, now looks like mousehandling i need to do :( , on double clicking group row is not toggling

Related

Show only the first row in a UITableView

So, most of the questions are "my tableView only shows the first row, what's wrong?".
Well, what I need is exactly the opposite.
I have a SearchBar (not Search Display Controller) and until the user starts typing, I want to show ONLY the first row and nothing more.
My TableView's content is Dynamic Prototypes, with 2 Prototype Cells.
The first is the only one I want to show, but it shows others in blank.
https://www.dropbox.com/s/q4ak6z3gbc0gh5c/Screenshot%202014-07-22%2011.24.22.png
This is my tableView:numberOfRowsInSection:.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger numberOfRows = 0;
if ([self.searchBar.text isEqualToString:#""]) {
numberOfRows = 1;
}
return numberOfRows;
}
All the help will be very appreciated! \o/
Probably there is a better solution, but as proposed before :
CGRect frame = [self.tableView frame];
frame.size.height = [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
self.tableView.frame = frame;
In your view controller's -tableView:numberOfRowsInSection: make sure tableView equals to self.tableView, and then you return 1:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.tableView) {
return 1;
} else {
return yourDataModel.count;
}
}
You must change the size UITableView's contentView or frame at the creation (alloc).
This height must be the same of an UITableViewCell by default (44px) or custom.
Write the below 4 lines to make a table with one row. If you want to extend the content of the table just make the tableview scrollable.
tblView = [[UITableView alloc]initWithFrame:CGRectZero];
tblView.separatorColor = [UIColor clearColor];
tblView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
tblView.scrollEnabled = NO;

UITableViewCell content overlaps delete button when in editing mode in iOS7

I am creating a UITableView with custom UITableViewCells. iOS 7's new delete button is causing some problems with the layout of my cell.
If I use the "Edit" button, which makes the red circles appear I get the problem, but if I swipe a single cell it looks perfect.
This is when the Edit button is used:
[self.tableView setEditing:!self.tableView.editing animated:YES];
This is when I swipe a single cell:
As you can se my labels overlaps the delete button in the first example. Why does it do this and how can I fix it?
try using the accessoryView and editingAccessoryView properties of your UITableViewCell, instead of adding the view yourself.
If you want the same indicator displayed in both editing and none-editing mode, try setting both view properties to point at the same view in your uiTableViewCell like:
self.accessoryView = self.imgPushEnabled;
self.editingAccessoryView = self.imgPushEnabled;
There seems to be a glitch in the table editing animation in IOS7, giving an overlap of the delete button and the accessoryView when switching back to non-editing state. This seems to happen when the accesoryView is specified and the editingAccessoryView is nil.
A workaround for this glitch, seems to be specifying an invisible editingAccessoryView like:
self.editingAccessoryView =[[UIView alloc] init];
self.editingAccessoryView.backgroundColor = [UIColor clearColor];
The problem is that in edit mode the cell's contentView changes in size. So either you have to override layoutSubviews in your cell and support the different frame sizes
- (void) layoutSubviews
{
[super layoutSubviews];
CGRect contentFrame = self.contentView.frame;
// adjust to the contentView frame
...
}
or you take the bait and switch to autolayout.
First I thought setting contentView.clipsToBounds to YES could be an ugly workaround but that does not seem to work.
I've resolved this problem with set up constraints without width only leading and trailing
As tcurdt mentioned, you could switch to autolayout to solve this issue. But, if you (understandably) don't want to mess with autolayout just for this one instance, you can set the autoresizingMask and have that turned automatically into the appropriate autolayout constraints.
label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
Just use this method in your custom TableViewCell class you can get the perfect answer,
Here self is UITableviewCell
- (void)layoutSubviews {
[super layoutSubviews];
for (UIView *subview in self.subviews) {
for (UIView *subview2 in subview.subviews) {
if ([NSStringFromClass([subview2 class]) isEqualToString:#"UITableViewCellDeleteConfirmationView"]) { // move delete confirmation view
[subview bringSubviewToFront:subview2];
}
}
}
}
And if any one want to adjust the Delete Button Size, Use the following Code
- (void)layoutSubviews {
[super layoutSubviews];
for (UIView *subview in self.subviews) {
for (UIView *subview2 in subview.subviews) {
if ([NSStringFromClass([subview2 class]) isEqualToString:#"UITableViewCellDeleteConfirmationView"]) { // move delete confirmation view
CGRect rect = subview2.frame;
rect.size.height = 47; //adjusting the view height
subview2.frame = rect;
for (UIButton *btn in [subview2 subviews]) {
if ([NSStringFromClass([btn class]) isEqualToString:#"UITableViewCellDeleteConfirmationButton"]) { // adjusting the Button height
rect = btn.frame;
rect.size.height = CGRectGetHeight(subview2.frame);
btn.frame = rect;
break;
}
}
[subview bringSubviewToFront:subview2];
}
}
}
}
Best way to remove this problem is that add an image in cell and set it in Backside.
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"bgImg.png"]];
imageView.frame = CGRectMake(0, 0, 320, yourCustomCell.frame.size.height);
[yourCustomCell addSubview:imageView];
[yourCustomCell sendSubviewToBack:imageView];
If your text would overlap the delete button then implement Autolayout. It'll manage it in better way.
One more case can be generate that is cellSelectionStyle would highlight with default color. You can set highlight color as follows
yourCustomCell.selectionStyle = UITableViewCellSelectionStyleNone;
Set your table cell's selection style to UITableViewCellSelectionStyleNone. This will remove the blue background highlighting or other. Then, to make the text label or contentview highlighting work the way you want, use this method in yourCustomCell.m class.
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
if (highlighted)
self.contentView.backgroundColor = [UIColor greenColor];
else
self.contentView.backgroundColor = [UIColor clearColor];
}
I hope you understand it in a better way.
Bringing to front UITableViewCellDeleteConfirmationView in the layoutSubviews of the custom cell works for me on iPhone, but not on iPad.
I have a UITableView in the master part of a splitViewController for the iPad, and in this case
the frame of the UITableViewCellDeleteConfirmationView is (768 0; 89 44), instead of (320 0; 89 44)
So I resize the frame in the layoutSubviews method and this works for me
- (void)layoutSubviews {
[super layoutSubviews];
for (UIView *subview in self.subviews)
{
for (UIView *subview2 in subview.subviews)
{
if ([NSStringFromClass([subview2 class]) isEqualToString:#"UITableViewCellDeleteConfirmationView"])
{
CGRect frame = subview2.frame;
frame.origin.x = 320;
subview2.frame = frame;
[subview bringSubviewToFront:subview2];
}
}
}
}
If you are putting content in the UITableViewCell's contentView, be sure you use self.contentView.frame.size.width and not self.frame.size.width in layoutSubviews.
self.frame expands width in editing mode, and will cause any content on the right to extend past the bounds of the contentView. self.contentView.frame stays at the correct width (and is what you should be using).
Try this: Might be you are setting cell setBackgroundImage in cellForRowAtIndexPath (Delegate Method). Do not set this here. Set your image in:
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { cell.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"cellList.png"]]; }
Enjoy Coding.
My solution is to move whole contentView to the left when Delete button showing:
override func layoutSubviews() {
super.layoutSubviews()
if editingStyle == UITableViewCellEditingStyle.Delete {
var rect = contentView.frame
rect.origin.x = self.showingDeleteConfirmation ? -15 : 38
contentView.frame = rect
}
}

Issues with NSOutlineView and ImageAndTextCell

I'm having an NSOutlineView with items (and children in it).
Here's the cell modification code :
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
if ([item isKindOfClass:[JQPage class]])
{
[cell setImage:[NSImage imageNamed:#"doc_empty_icon&16"] size:16.0];
}
else if ([item isKindOfClass:[JQElement class]])
{
[cell setImage:[NSImage imageNamed:#"brackets_icon&16"] size:16.0];
}
}
And here's a visual example of what I need :
Any ideas?
Im not sure about pushing the children to the right. You might have to overwrite som e class function.
You can swap the image to a white version when the item is selected. I don't know if NSOutlineView support Automatic Black/white swap, but if it does you have to set the background style with something like
-(void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle {
[super setBackgroundStyle:NSBackgroundStyleLight];
}
What you're looking for regarding the image is NSImage setTemplate. From the docs,
Cocoa cells take advantage of the nature of template images—that is, their simplified color scheme and use of transparency—to improve the appearance of the corresponding control in each of its supported states.
In this case the image will be swapped with its negative on selection.

How do I change the color or a NSTableCellView for each cell of an NSOutlineView

I've looked at a number of questions but can't find a good solution for a View-Based NSOutlineView
Coloring NSTableView Text per row
Change color of NSTableViewCell
Custom background colors for NSTableCellView
I'm trying to set each row to whatever color I want. I've read somewhere that I need to subclass NSTableRowView which I've now done.
According to the AppleDocs, I see the following methods:
– drawBackgroundInRect:
– drawDraggingDestinationFeedbackInRect:
– drawSelectionInRect:
– drawSeparatorInRect:
How would I go about setting the background color for the individual rows? Am I going the wrong route above?
Edit: below (also edited title)
Since i'm using an NSOutlineView and not a NSTableView, when i change the background color of the cells the image looks like the following. The disclosure arrows to the left is not colored. Is there any way to change the color of the whole row for the NSOutlineView?
You could subclass NSTableViewCell, and add a method to it which sets its color.
NSTableViewCell is already a subclass of NSView, so in your subclass, you would add the following method:
- (void)setBackgroundColor {
self.layer.backgroundColor = CGColorCreateGenericRGB(0, 0, 0, 1.0f); // or whatever color
}
Or something like that. You'll probably want to have the color be a param to the method. Then, in your table view delegate, you can set the color depending on the row index passed to the delegate method. For example:
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [table dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
if (indexPath.row % 2) {
[cell setBackgroundColor:[UIColor redColor]]; // or something like that
}
}
Came up with a solution. Implemented the following.
-(void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row {
[self updateRowViewBackColorforItem:[outlineView itemAtRow:row]];
}
-(void)updateRowViewBackColorforStep:(myCustomItem *)customItem {
static NSColor *color1;
static NSColor *color2;
static NSColor *color3;
if (color1 == nil) {
sharedcolorHeader = [NSColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0];
}
if (color2 == nil) {
sharedcolorChildren = [NSColor colorWithCalibratedRed:(x/255.0f) green:(y/255.0f) blue:(z/255.0f) alpha:1.0];
}
if (color3 == nil) {
normalColor = [NSColor colorWithCalibratedRed:(255/255.0f) green:(255/255.0f) blue:(255/255.0f) alpha:1.0];
}
NSInteger row = [stepOutlineView rowForItem:step];
if (row < 0) return;
NSTableRowView *view = [myOutlineView rowViewAtRow:row makeIfNecessary:NO];
if ([customItem type] == 1) {
[view setBackgroundColor:sharedcolorHeader];
} else if([customItem type] == 2) {
[view setBackgroundColor:sharedcolorChildren];
} else {
[view setBackgroundColor:normalColor];
}
}
This is really something that should rely on properties or ivars in your data model.
If you use view based outline views, you can simply have custom views for your row views and or cell views.
Have the custom views draw whatever you want based in the data in your represented object.

NSOutlineView Drag-n-Drop

In my application, NSOutlineView used as below,
1 -- Using CustomOutlineView because i want to control NSOutlineView Background,
2 -- Using CustomeCell, becuase i need to have customize Cell
Header File : MyListView.h
/*
MyUICustomView is the Subclass from NSView and this is i need to have
for some other my application purpose
*/
#interface MyListView : MyUICustomView<NSOutlineViewDataSource>
{
// MyCustomOutlineview because, i need to override DrawRect Method to have
// customized background
MyCustomOutlineView *pMyOutlineView;
}
#property(nonatomic,retain)MyCustomOutlineView *pMyOutlineView;
and also i should be able to drag-n-drop within Outline view,
for having drag-n-drop i have done following,
-(void)InitOutlineView{
// Creating outline view
NSRect scrollFrame = [self bounds];
NSScrollView* scrollView = [[[NSScrollView alloc] initWithFrame:scrollFrame] autorelease];
[scrollView setBorderType:NSNoBorder];
[scrollView setHasVerticalScroller:YES];
[scrollView setHasHorizontalScroller:NO];
[scrollView setAutohidesScrollers:YES];
[scrollView setDrawsBackground: NO];
NSRect clipViewBounds = [[scrollView contentView] bounds];
pMyOutlineView = [[[MyCustomOutlineView alloc] initWithFrame:clipViewBounds] autorelease];
NSTableColumn* firstColumn = [[[NSTableColumn alloc] initWithIdentifier:#"firstColumn"] autorelease];
#ifdef ENABLE_CUSTOM_CELL
// Becuase cell should have Image, Header Info and brief detail in small font,
// so i need to have custom cell
ImageTextCell *pCell = [[ImageTextCell alloc]init];
[firstColumn setDataCell:pCell];
// SO i can fill the data
[pCell setDataDelegate:self];
# endif
[pMyOutlineView setDataSource:self];
/* This is to tell MyCustomOutlineView to handle the context menu */
[pMyOutlineView setDataDelegate:self];
[scrollView setDocumentView:pCTOutlineView];
[pMyOutlineView addTableColumn:firstColumn];
[pMyOutlineView registerForDraggedTypes:
[NSArray arrayWithObjects:OutlinePrivateTableViewDataType,nil]];
[pMyOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];**
}
and to Support drag-n-drop Implemented following method
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:items];
[pboard declareTypes:[NSArray arrayWithObject:OutlinePrivateTableViewDataType] owner:self];
[pboard setData:data forType:OutlinePrivateTableViewDataType];
return YES;
}
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index{
// Add code here to validate the drop
NSLog(#"validate Drop");
return NSDragOperationEvery;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index{
NSLog(#"validate Drop");
}
but still when i try to drag the row of NSOutlineView nothing is happening, even i tried to debug via NSLog but i couldn't see any log from above function,
Am i missing any important method ?
to support Drag-n-drop
Based on your code, it looks like you're not returning anything for the ...writeItems... method. This method is supposed to return a BOOL (whether the items were successfully written or you're denying the drag). Returning nothing should be giving you a compiler warning ...
HI,
FInally got able to rid of this,
The problem is
[firstColumn setWidth:25];
So drag was working only upto 25 pixel form the left, and i commented this, then its working time, but strange to see, my outline view has only one column , for drawing, its not checking the restriction but for drag-n-drop its checking,
Thanks Joshua