custom NSTableViewCell - cocoa

I custom NSTableView just like what I do in UITableView. I implement the datasource and delegate. In the code, I do like this:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
ZJCellView *cellView = [tableView makeViewWithIdentifier:cellIdentifier owner:self];
//this is a custom view
if (cellView == nil)
{
CGRect rect = CGRectMake(0, 0, tableView.frame.size.width, kTableViewCellHeight);
cellView = [[[ZJCellView alloc] initWithFrame:NSRectFromCGRect(rect)] autorelease];
cellView.identifier = cellIdentifier;
// some other controls
}
return cellView;
I do the custom just like what I do in iOS.
The problem is there is a line in both left and right border, like:
I tried change the frame of the cell view and it seems to be of no use.
As it is the right way to custom the cell in iOS, I wonder where is it wrong in Cocoa.
Can any body help me?
thanks.

Finally, I find it is the Cell Space which can be set in the nib.

In iOS:
Tableviewcells have same with with Tableview. They are autoresized. If you set frame for a cell in cellForRowAtIndexPath: method, changes nothing.
For width you need to change the width of the tableview.
For height you need to implement delegate method heightForRowAtIndexPath: of the tableview or set rowHeight property.
But I don't know how is it for NSTableView, I think it is similar.

Related

Programmatically created UICollectionView: cells overlap after scrolling

I'm trying to implement a custom UICollectionView that shows all elements from the asset library. While displaying the items is not that big of a deal, I run into a weird bug which I think has to do something with the layout I'm using.
Let's start with some screenshots to indicate what's going on. I'll just post the links to these images to be easy on your scroll wheels :-)
1. Initial
https://dl.dropboxusercontent.com/u/607872/stackoverflow/01-uicollectionview-start.png
Everything ok.
2. Scrolled down
https://dl.dropboxusercontent.com/u/607872/stackoverflow/02-uicollectionview-scrolleddown.png
The images from the first row (which isn't visible anymore) repeat behind the images in the last row. The first one is invisible because it is entirely overlapped.
3. Scrolled up again
https://dl.dropboxusercontent.com/u/607872/stackoverflow/03-uicollectionview-scrollup.png
The first image (in the background) is from the same row, but should be the third. The 2nd and 3rd image are from the last row.
Here's some code. I hope I didn't miss any relevant stuff - if so, please let me know.
MultiImagePickerController.m (:UICollectionViewController, protocols: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout)
- (void)loadView {
[...] // layout stuff
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
[layout setScrollDirection:UICollectionViewScrollDirectionVertical];
_collectionView = [[UICollectionView alloc] initWithFrame:collectionRect collectionViewLayout:layout];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView registerClass:[MultiImagePickerCell class] forCellWithReuseIdentifier:#"MultiImagePickerCellIdentifier"];
[_collectionView setBackgroundColor:[UIColor whiteColor]];
[self.view addSubview:_collectionView];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MultiImagePickerCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"MultiImagePickerCellIdentifier" forIndexPath:indexPath];
if ( !cell ) {
CGSize sizeCell = [MultiImagePickerCell defaultSize];
cell = [[MultiImagePickerCell alloc] initWithFrame:CGRectMake( 0, 0, sizeCell.width, sizeCell.height )];
}
NSString *groupname = [self getKeyFromMutableDictionary:assetList forIndex:indexPath.section];
[cell addImageUsingAsset:[[assetList objectForKey:groupname] objectAtIndex:indexPath.row]];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize sizeCell = [MultiImagePickerCell defaultSize];
return CGSizeMake( sizeCell.width + 10.0, sizeCell.height + 10.0 );
}
MultiImagePickerCell - (void)addImageUsingAsset:(ALAsset *)asset;
This method adds a UIImageView and a UIImage to the cell to show the asset. Nothing special, in my opinion. When I set breakpoints in cellForItemAtIndexPath, I can see that the correct asset is being read from the assetList and the views are calculated correctly. The drawRect method of the cell is NOT implemented.
I think I experienced the same problem a few years back when I tried to programmatically create a UITableViewController, but I can't remember how I solved it.
Any suggestions?
Got it - I had to remove the subviews from my cells.

How to add subview in ListView?

I am developing my first MAC application, i downloaded one Example of PxListView
and i have to added one button and background image on cell xib and bind them with controller
and, when on button click i was set height of that cell is much bigger then other. that is done,
and work fine.
but now i want to develop like after is witch cell has open in that cell i want to add some extra contain (Controller) on it, so how it will possible using given example?
pls help me to give some suggest how it will be done.
for Ex like before click on button
after chick on button i want to develop like
You write
i have to added one button and background image on cell xib and bind them with controller
It sounds like you've subclassed PXListViewCell--for convenience, let's call your subclass TemplateListViewCell--and added a xib from which instances of TemplateListViewCell will be loaded in
+[PXListViewCell cellLoadedFromNibNamed:bundle:reusableIdentifier:]
In addition, there is a[t least one] button in TemplateListViewCell.xib.
You write
when on button click i was set height of that cell is much bigger then other. that is done, and work fine
It sounds like this button has as its action a method on TemplateListViewCell such as
- (IBAction)toggleDetail:(id)sender
{
//Code to grow or shrink the height of [self frame].
//...
}
In my approach to implementing -toggleDetail, two modifications to the PXListView files were necessary:
1. Adding a protocol method
- (void)listView:(PXListView *)aListView setHeight:(CGFloat)height ofRow:(NSUInteger)row;
to the PXListViewDelegate protocol.
2. Adding a property
#property (nonatomic, assign) BOOL expanded;
to PXListViewCell.
My implementation of -toggleDetail looks something like this:
- (IBAction)toggleDetail:(id)sender
{
BOOL wasExpanded = [self expanded];
NSRect oldFrame = [self frame];
CGFloat oldHeight = oldFrame.size.height;
CGFloat newHeight = oldHeight;
CGFloat heightIncrement = 0.0f;
if (wasExpanded) {
heightIncrement = -80.0f; //use whatever value is appropriate
} else {
heightIncrement = 80.0f; //use whatever value is appropriate
}
newHeight += heightIncrement;
[[[self listView] delegate] listView:[self listView] setHeight:newHeight ofRow:[self row]];
[[self listView] reloadData];
BOOL isExpanded = !wasExpanded;
[self setExpanded:isExpanded];
}
It might seem better to use [[self listView] reloadRowAtIndex:[self row]]; in place of [[self listView] reloadData], but unfortunately, this doesn't work: if the user hides the detail--shrinks the cell vertically--new cells which should appear on the screen do not.
You write
that is done, and work fine.
It sounds like you were able to implement successfully a method analogous to -[TemplateListViewCell toggleDetail:].
You write
but now i want to develop like after is witch cell has open in that cell i want to add some extra contain (Controller) on it, so how it will possible using given example? pls help me to give some suggest how it will be done.
It sounds like you want instances of TemplateListViewCell to contain extra views if they are expanded.
It might seem tempting to put this code into -[TemplateListViewCell toggleDetail], but this will not work out as we might hope. The trouble is, we need to handle cases where expanded cells have been scrolled out of view and scrolled back into view.
To get this right, we need to have a notion of expanded which persists beyond the usage of a PXListViewCell subclass instance: we either need to keep track of expansion in the PXListView itself or in its delegate.
The better--but less expedient--design seems to be to keep track of this information in the PXListView itself. For the sake of this question, however, I'll demonstrate how to keep track of cell expansion in the delegate. To do this, I'm expanding the PXListViewDelegate protocol and making other changes to the PXListView files:
1. Adding the methods
- (void)listView:(PXListView *)aListView setExpanded:(BOOL)expanded atRow:(NSUInteger)row;
- (BOOL)listView:(PXListView *)aListView expandedAtRow:(NSUInteger)row;
to PXListViewDelegate.
2. Adding the method
- (void)setCell:(PXListViewCell *)cell expandedAtRow:(NSUInteger)row
{
if ([[self delegate] respondsToSelector:#selector(listView:expandedAtRow:)]) {
[cell setExpanded:[[self delegate] listView:self expandedAtRow:row]];
}
}
to PXListView.
3. Calling -[PXListView setCell:expandedAtRow:] from -[PXListView layoutCells]
- (void)layoutCells
{
//Set the frames of the cells
for(id cell in _visibleCells)
{
NSInteger row = [cell row];
[cell setFrame:[self rectOfRow:row]];
[self setCell:cell expandedAtRow:row];
[cell layoutSubviews];
}
NSRect bounds = [self bounds];
CGFloat documentHeight = _totalHeight>NSHeight(bounds)?_totalHeight:(NSHeight(bounds) -2);
//Set the new height of the document view
[[self documentView] setFrame:NSMakeRect(0.0f, 0.0f, NSWidth([self contentViewRect]), documentHeight)];
}
and from -[PXListView layoutCell:atRow:]:
- (void)layoutCell:(PXListViewCell*)cell atRow:(NSUInteger)row
{
[[self documentView] addSubview:cell];
[cell setFrame:[self rectOfRow:row]];
[cell setListView:self];
[cell setRow:row];
[cell setHidden:NO];
[self setCell:cell expandedAtRow:row];
}
4. Setting _expanded to NO in -[PXListViewCell prepareForReuse]:
- (void)prepareForReuse
{
_dropHighlight = PXListViewDropNowhere;
_expanded = NO;
}
Note: In the sample PXListViewCell subclass, MyListViewCell, distributed with PXListView, the implementation of -[MyListViewCell prepareForReuse] fails to call [super prepareForReuse]. Make sure that this call is made in [TemplateListViewCell prepareForReuse]:
- (void)prepareForReuse
{
//...
[super prepareForReuse];
}
One change needs to be made to -[TemplateListViewCell toggleDetail:]. The line
[self setExpanded:isExpanded];
needs to be replaced by
[[[self listView] delegate] listView:[self listView] setExpanded:isExpanded atRow:[self row]];
Once you've set up your PXListView's delegate to properly handle the new delegate methods, you're ready to override [PXListViewCell setExpanded:] in your subclass TemplateListViewCell:
- (void)setExpanded:(BOOL)expanded
{
if (expanded) {
//add detail subviews
} else {
//remove detail subviews
}
[super setExpanded:expanded];
}
Replace //add detail subviews with your own code which programmatically adds the detail subviews that you want and replace //remove detail subviews with code to remove the detail subviews that you want, checking to see that they are present first.
You write
i want to add some extra contain (Controller) on it
It sounds like you want to add view controllers rather than views to your TemplateListViewCell. To do this, use an NSBox and set the box's contentView to your view controller's view. (For details on this, see this answer.)
If you plan on just showing a single view controller's view in an NSBox on the expanded TemplateListViewCell, you can just (1) add a property to TemplateListViewCell referencing your view controller and (2) add an NSBox to TemplateListViewCell xib and set its contentView to the appropriate view controller's view on [cell setExpanded:YES] and set its contentView to nil on [cell setExpanded:NO].

How to use Storyboard to make popover that can be used in code?

I'm building a collection of forms each of which contains several fields. Some of the fields are UITextFields that will display a date. I've created a new class called DatePickerTextField, a descendant of UITextField. When a DatePickerTextField is tapped I'd like for a UIDatePicker control to appear in a popover.
My question is how do I use the storyboard to implement the popover? I can do a segue when there is a specific, visible control in the scene. But how do I represent a generic popover in the scene that I can attach to any instantiated DatePickerTextField that becomes active?
You can create segue that is not connected to any control but I don't think that there would be way to specify anchor point for popover from code. Another option is to create ViewController that is not connected with any segue. When editing storyboard, create ViewController which will be placed in popover, select it and navigate to Utilities pane->Attributes Inspector. Set Size to Freeform, Status Bar to None, specify unique Identifier that will be used to instantiate ViewController from code. Now you can change the size of ViewController by selecting its View and navigating to Utilities pane->Size Inspector.
After that you can create popover from code:
- (IBAction)buttonPressed:(id)sender {
UIView *anchor = sender;
UIViewController *viewControllerForPopover =
[self.storyboard instantiateViewControllerWithIdentifier:#"yourIdentifier"];
popover = [[UIPopoverController alloc]
initWithContentViewController:viewControllerForPopover];
[popover presentPopoverFromRect:anchor.frame
inView:anchor.superview
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
One caveat is that you need to hold reference to popover as ivar of your class, otherwise it'll crash because UIPopoverController would be released and deallocated after buttonPressed returns:
#interface MyViewController : UIViewController {
// ...
UIPopoverController *popover;
// ...
}
So, I had a similar issue, and in case others might benefit, I figured I'd share it, since I benefit so much from stackoverflow.
This solution allows you to set the anchor of a customizable popover segue. It also allows you to configure the segue to be modal or not (I could not find a way to prevent the segue by dimming the exterior context, so if someone knows how to do that, I would be interested in hearing it); this is accomplished by setting the passthrough views for the popover controller. I also added the capacity to specify a custom view, rather than the view of the source viewcontroller (since I needed this capacity); this portion is not critical to the solution.
DynamicPopoverSegue.h
#interface DynamicPopoverSegue : UIStoryboardPopoverSegue
#property BOOL isModal;
#property UIView* sourceView;
#property CGRect anchor;
#end
DynamicPopoverSegue.m
#implementation DynamicPopoverSegue
- (void)perform
{
if (!self.popoverController.popoverVisible)
{
UIViewController* dst = (UIViewController*)self.destinationViewController;
UIViewController* src = (UIViewController*)self.sourceViewController;
UIView* inView = _sourceView ? _sourceView : src.view;
self.popoverController.contentViewController = dst;
if (!_isModal)
{
[self.popoverController setPassthroughViews:[[NSArray alloc] initWithObjects:inView, nil]];
}
[self.popoverController presentPopoverFromRect:_anchor
inView:inView
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
}
#end
Then you just set your segue to "Custom" in the storyboard, and set the segue class to "DynamicPopoverSegue". In my case, since I wanted to associate it with dynamic layers in a view, I could not set the anchor, so I created the segue by control clicking from the view controller icon in the bar beneath my view controller to the view controller I was using to present the popupover.
To call the popover segue:
[self performSegueWithIdentifier:#"MyPopoverSegue" sender:self];
And to configure the popover segue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"MyPopoverSegue"])
{
DynamicPopoverSegue* popoverSegue = (DynamicPopoverSegue*)segue;
// set the anchor to wherever you want it to be
popoverSegue.anchor = _destinationFrame;
}
}
- (IBAction)pressItemChooseOprateRoom:(id)sender {
if (isPad){
// UIView *anchor = sender;
UIViewController *viewControllerForPopover =
[self.storyboard instantiateViewControllerWithIdentifier:#"OperateRoomList"];
_myPopover = [[UIPopoverController alloc]
initWithContentViewController:viewControllerForPopover];
CGRect rc=[self getBarItemRc:sender];
[_myPopover presentPopoverFromRect:rc
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[MLControl shared].popover =self;
// [self perfformSegueWithIdentifier:SEGUE_POP_OPERATEROOM sender:self];
}else{
[self iphoneOpenOperateRoomList];
/* [self performSegueWithIdentifier:#"iPhonePushOperateRoom" sender:self];
*/
}
}
-(void)iphoneOpenOperateRoomList{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"OperateRoomList"];
// if (!index.showTabBar) {
// vc.hidesBottomBarWhenPushed = YES;
// }
[self.navigationController pushViewController:vc animated:YES];
}
Just used the answer from Jonnywho for my SWIFT project. In case you need it:
Here's the SWIFT version:
let anchor: UIView = sender
var viewControllerForPopover = self.storyboard?.instantiateViewControllerWithIdentifier("GameAboutViewController") as! UIViewController?
let popover = UIPopoverController(contentViewController: viewControllerForPopover!)
popover.presentPopoverFromRect(anchor.frame, inView: anchor, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
Add a UIView in the scene dock.
You can add it as a subview to any existing view on the view controller.
You can then toggle it's isHidden property as you require.
You can add multiple such subviews and create many such popups.
This technique will save you from setting up a new View Controller and using segues.

Coloring rows in View based NSTableview

I have a view based nstableview. I want to color entire row based on some condtion for which I have used code below
- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
{
NSTableRowView *view = [[NSTableRowView alloc] initWithFrame:NSMakeRect(1, 1, 100, 50)];
[view setBackgroundColor:[NSColor redColor]];
return view;;
}
The delegate method is called, but table doesn't seem to be using NSTableRowView returned by delegate method.
Main aim here is coloring entire row based on some condition. Whats wrong in above implementation?
For anyone else who hits this and wants a custom NSTableRowView backgroundColor, there are two approaches.
If you don't need custom drawing, simply set rowView.backgroundColor in - (void)tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row in your NSTableViewDelegate.
Example:
- (void)tableView:(NSTableView *)tableView
didAddRowView:(NSTableRowView *)rowView
forRow:(NSInteger)row {
rowView.backgroundColor = [NSColor redColor];
}
If you do need custom drawing, create your own NSTableRowView subclass with desired drawRect. Then, implement the following in NSTableViewDelegate:
Example:
- (NSTableRowView *)tableView:(NSTableView *)tableView
rowViewForRow:(NSInteger)row {
static NSString* const kRowIdentifier = #"RowView";
MyRowViewSubclass* rowView = [tableView makeViewWithIdentifier:kRowIdentifier owner:self];
if (!rowView) {
// Size doesn't matter, the table will set it
rowView = [[[MyRowViewSubclass alloc] initWithFrame:NSZeroRect] autorelease];
// This seemingly magical line enables your view to be found
// next time "makeViewWithIdentifier" is called.
rowView.identifier = kRowIdentifier;
}
// Can customize properties here. Note that customizing
// 'backgroundColor' isn't going to work at this point since the table
// will reset it later. Use 'didAddRow' to customize if desired.
return rowView;
}
Finally it worked as below
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSView *cellView = (NSView*) [tableView makeViewWithIdentifier:[tableColumn identifier] owner:[tableView delegate]];
CALayer *viewLayer = [CALayer layer];
[viewLayer setBackgroundColor:[[NSColor redcolor] CGColor]];
[cellView setWantsLayer:YES];
[cellView setLayer:viewLayer];
return cellView;
}
Please note.. u need to convert nscolor to cgcolor which you can find in https://gist.github.com/707921 or http://forrst.com/posts/CGColor_Additions_for_NSColor-1eW
If you watch the presentation on view based tableviews from WWDC 2011, you'll see that the main idea is to create the views in Interface Builder, and then obtain them from there. Something like:
[tableView makeViewWithIdentifier:#"GroupRow" owner:self];
Once you have obtained the view, just set its properties and return it.
Notice in this example that it has its own identifier, so remember to set that, but you can also used automatic identifiers.
I don't know if a direct link to the WWDC will work, but the main page is here: https://developer.apple.com/videos/wwdc/2011/ and if you search for "View Based NSTableView Basic to Advanced", you'll find it. It is well worth watching.
I re-wrote the layer approach.
In Swift 3.2
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let greenCell = self.tableview.make(withIdentifier: "green", owner: self)
let layer:CALayer = CALayer()
layer.backgroundColor = NSColor.green.cgColor
greenCell?.wantsLayer = true
greenCell?.layer = layer
return greenCell
}
Don't forget to change the Identifier of the cell according to your storyboard, and in the code identifier "green". And surely, the background color if you want.

How to make NSTableView transparent?

I want to make transparent NSTableView.
I am using WindowController class here.
I was trying this:
- (void)windowDidLoad
{
[super windowDidLoad];
[[self enclosingScrollView] setDrawsBackground: NO];
[[self enclosingScrollView] setBorderType:NSNoBorder];
}
- (BOOL)isOpaque {
return NO;
}
- (void)drawRect:(NSRect)drawRect
{
[super drawRect: drawRect];
}
But when i was writing this code i can't found enclosingScrollView in help window.
you can see here..
Any help?? Please remember me or correct me if i am doing something wrong.
Thank you.
If you have an NSScrollView enclosing your NSTableView, you can set the scroll view's drawsBackground property to NO like this:
yourScrollView.drawsBackground = NO;
Got Answer..!!! I just tried this
[tableview setBackgroundColor:[NSColor clearColor]];
[tableview setHeaderView:nil];
and its working fine.. – – Snehal
Copied from comment in question, as its a bit buried...
If your app needs to display a transparent table view, set the background color of the table view to be clear and set the enclosing scroll view to not draw its background. The following code snippet shows one way to display a transparent table:
Swift:
yourTableView.backgroundColor = NSColor.clear
yourTableView.enclosingScrollView?.drawsBackground = false
Objective-C
[theTableView setBackgroundColor:[NSColor clearColor];
[[theTableView enclosingScrollView] setDrawsBackground:NO];
Apple - Modifying a Table’s Visual Attributes

Resources