Position a sheet at a row in NSTableView - objective-c

I'm trying to position a sheet within an NSTableView just below a certain row. I have the row number and have been successfully able to position the sheet at the right column but not the right row.
Apparently NSRect is drawing up from the bottom of the window containing tableView and I can't figure out how to have it identify the row... I know how to position the sheet at a button or text field NSRect cellRect = [myButton frame]; but not at a row...
Any suggestions or pointers would be very helpful, thanks!
- (NSRect)window:(NSWindow *)window willPositionSheet:(NSWindow *)sheet usingRect:(NSRect)rect {
//the row number is stored as a user default - convoluted but it is what it is
int rowNumber = [[[NSUserDefaults standardUserDefaults] stringForKey:#"p"] intValue];
NSRect cellFrame = [tableView frameOfCellAtColumn:2 row:rowNumber];
cellFrame.size.height = 0;
return cellFrame;
}

Related

Saving all Custom XIB Cell TableField Data into an Array

I have a UITableView that displays a custom XIB cell repeated for all the data in an array I have. I also have a UIBarButton item that when pressed, adds a blank entry into that data array, which then creates a new blank cell. (Because numberOfRowsInSection returns [self.array count])
The XIB cell has a clear UITextField, so all cells are editable. The textfields, for each cell, displays that data in the array, for the appropriate index.
Now, what I'm trying to do is, when the UIBarButton item 'Save' is pressed, it gathers all of the data in the cells textfields, and basically saves it all into an array. I can't find a solution anywhere, so any help is appreciated. Thanks in advance!
EDIT: I solved my own problem. For those wondering what to do:
for (int i = 0; i < [self.dataArray count]; i++) {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
for (id subview in [cell.contentView subviews]) {
if ([subview isKindOfClass:[UITextField class]]) {
UITextField *textField = (UITextField *)subview;
self.updatedArray = [NSMutableArray arrayWithObject:textField.text];
NSLog(#"Cell Text: %#", self.updatedArray);
}
}
}
You could just loop through all the cells in a table view and add the contents of the text field to an array.
The example is assuming all the cells are in section 0 but you could change it to a nested loop with the number of sections playing a factor.
var saveData = [String]();
for i in 0..<tableView.numberOfRowsInSection(0){
let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: i, inSection: 0)) as! NameOfYourSubclassedCell;
saveData.append(cell.textField.text);
}

Objective c - Expand cell height on runtime [duplicate]

This question already has answers here:
Can you animate a height change on a UITableViewCell when selected?
(21 answers)
ios - Expand table view cell like Twitter app
(3 answers)
Closed 9 years ago.
I have a custom cell and one of its labels can be larger enough to justify adding a "See more" button. So I'm cutting off the label's content to 5 lines and then adding this button that expands the content, if needed.
My question is: Can this be done without using reloadData of the tableView?
I have tried this code on the touch event of the "See more" button (inside the custom cell):
self.contentView.frame = self.backgroundView.frame = self.accessoryView.frame = CGRectMake(0, 0, self.contentView.frame.size.width, self.contentView.frame.size.height + 80);
Nothing happens! I think I need a way to expand the cell row height and so the table view's content height.
Thanks in advance,
Edition: I checked the solution in link ios - Expand table view cell like Twitter app, I have applied this code in the custom cell (on the touch event):
self.contentExpanded = YES;
UITableView *tableView = [self parentTableView];
NSIndexPath *indexPath = [tableView indexPathForCell:self];
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
I'm having 2 issues now:
1) The reloadRowsAtIndexPaths is performing a whole reloadData.
2) The custom cell is the one who knows the cell's height. When accesing this method, self.contentExpanded is always equal to NO.
***The method parentTableView was taken from Reference to the parent tableView of a cell
Edition 2: I dont think the issue is on the heightForRow. I can't avoid this method being called for all of the cells...
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *) indexPath
{
CellObject *object;
if(self.searchTableData)
object = [self.searchTableData objectAtIndex:indexPath.row];
else
object = [self.database.cellObjectsFetchedResultsController objectAtIndexPath:indexPath];
id cell = [TableViewCellFactory createFromCellObject:object fromPrototypeTable:self.tableView];
CGFloat height = [cell getHeightRowForCellObject:object];
return height;
}
And in the custom cell:
- (CGFloat)getHeightRowForCellObject:(CellObject *)cellObject
{
CGFloat messageHeight = 0;
if (![cellObject.content isEqualToString:#""])
{
UILabel *tempMessageLabel = [[UILabel alloc] init];
tempMessageLabel.font = [UIFont systemFontOfSize:14];
tempMessageLabel.autoresizingMask = (UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin );
tempMessageLabel.numberOfLines = 5;
tempMessageLabel.text = cellObject.content;
[tempMessageLabel sizeToFit];
CGFloat lineHeight = [tempMessageLabel.text sizeWithFont:tempMessageLabel.font].height;
CGSize size = [tempMessageLabel.text sizeWithFont:tempMessageLabel.font
constrainedToSize:CGSizeMake(280, lineHeight * tempMessageLabel.numberOfLines)
lineBreakMode:UILineBreakModeWordWrap];
messageHeight = size.height;
if(self.contentExpanded)
messageHeight += 100;
}
return messageHeight;
}

Automatically adjust height of a NSTableView

I have asked this question once before, but I'm just not very satisfied with the solution.
Automatically adjust size of NSTableView
I want to display a NSTableView in a NSPopover, or in a NSWindow.
Now, the window's size should adjust with the table view.
Just like Xcode does it:
This is fairly simple with Auto Layout, you can just pin the inner view to the super view.
My problem is, that I can't figure out the optimal height of the table view.
The following code enumerates all available rows, but it doesn't return the correct value, because the table view has other elements like separators, and the table head.
- (CGFloat)heightOfAllRows:(NSTableView *)tableView {
CGFloat __block height;
[tableView enumerateAvailableRowViewsUsingBlock:^(NSTableRowView *rowView, NSInteger row) {
// tried it with this one
height += rowView.frame.size.height;
// and this one
// height += [self tableView:nil heightOfRow:row];
}];
return height;
}
1. Question
How can I fix this? How can I correctly calculate the required height of the table view.
2. Question
Where should I run this code?
I don't want to implement this in a controller, because it's definitely something that the table view should handle itself.
And I didn't even find any helpful delegate methods.
So I figured best would be if you could subclass NSTableView.
So my question 2, where to implement it?
Motivation
Definitely worth a bounty
This answer is for Swift 4, targeting macOS 10.10 and later:
1. Answer
You can use the table view's fittingSize to calculate the size of your popover.
tableView.needsLayout = true
tableView.layoutSubtreeIfNeeded()
let height = tableView.fittingSize.height
2. Answer
I understand your desire to move that code out of the view controller but since the table view itself knows nothing about the number of items (only through delegation) or model changes, I would put that in the view controller. Since macOS 10.10, you can use preferredContentSize on your NSViewController inside a popover to set the size.
func updatePreferredContentSize() {
tableView.needsLayout = true
tableView.layoutSubtreeIfNeeded()
let height = tableView.fittingSize.height
let width: CGFloat = 320
preferredContentSize = CGSize(width: width, height: height)
}
In my example, I'm using a fixed width but you could also use the calculated one (haven't tested it yet).
You would want to call the update method whenever your data source changes and/or when you're about to display the popover.
I hope this solves your problem!
You can query the frame of the last row to get the table view's height:
- (CGFloat)heightOfTableView:(NSTableView *)tableView
{
NSInteger rows = [self numberOfRowsInTableView:tableView];
if ( rows == 0 ) {
return 0;
} else {
return NSMaxY( [tableView rectOfRow:rows - 1] );
}
}
This assumes an enclosing scroll view with no borders!
You can query the tableView.enclosingScrollView.borderType to check whether the scroll view is bordered or not. If it is, the border width needs to be added to the result (two times; bottom and top). Unfortunately, I don't know of the top of my head how to get the border width.
The advantage of querying rectOfRow: is that it works in the same runloop iteration as a [tableView reloadData];. In my experience, querying the table view's frame does not work reliably when you do a reloadData first (you'll get the previous height).
Interface Builder in Xcode automatically puts the NSTableView in an NSScrollView. The NSScrollView is where the headers are actually located. Create a NSScrollView as your base view in the window and add the NSTableView to it:
NSScrollView * scrollView = [[NSScrollView alloc]init];
[scrollView setHasVerticalScroller:YES];
[scrollView setHasHorizontalScroller:YES];
[scrollView setAutohidesScrollers:YES];
[scrollView setBorderType:NSBezelBorder];
[scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
NSTableView * table = [[NSTableView alloc] init];
[table setDataSource:self];
[table setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle];
[scrollView setDocumentView:table];
//SetWindow's main view to scrollView
Now you can interrogate the scrollView's contentView to find the size of the NSScrollView size
NSRect rectOfFullTable = [[scrollView contentView] documentRect];
Because the NSTableView is inside an NSScrollView, the NSTableView will have a headerView which you can use to find the size of your headers.
You could subclass NSScrollView to update it's superview when the table size changes (headers + rows) by overriding the reflectScrolledClipView: method
I'm not sure if my solution is any better than what you have, but thought I'd offer it anyway. I use this with a print view. I'm not using Auto Layout. It only works with bindings – would need adjustment to work with a data source.
You'll see there's an awful hack to make it work: I just add 0.5 to the value I carefully calculate.
This takes the spacing into account but not the headers, which I don't display. If you are displaying the headers you can add that in the -tableView:heightOfRow: method.
In NSTableView subclass or category:
- (void) sizeHeightToFit {
CGFloat height = 0.f;
if ([self.delegate respondsToSelector:#selector(tableView:heightOfRow:)]) {
for (NSInteger i = 0; i < self.numberOfRows; ++i)
height = height +
[self.delegate tableView:self heightOfRow:i] +
self.intercellSpacing.height;
} else {
height = (self.rowHeight + self.intercellSpacing.height) *
self.numberOfRows;
}
NSSize frameSize = self.frame.size;
frameSize.height = height;
[self setFrameSize:frameSize];
}
In table view delegate:
// Invoke bindings to get the cell contents
// FIXME If no bindings, use the datasource
- (NSString *) stringValueForRow:(NSInteger) row column:(NSTableColumn *) column {
NSDictionary *bindingInfo = [column infoForBinding:NSValueBinding];
id object = [bindingInfo objectForKey:NSObservedObjectKey];
NSString *keyPath = [bindingInfo objectForKey:NSObservedKeyPathKey];
id value = [[object valueForKeyPath:keyPath] objectAtIndex:row];
if ([value isKindOfClass:[NSString class]])
return value;
else
return #"";
}
- (CGFloat) tableView:(NSTableView *) tableView heightOfRow:(NSInteger) row {
CGFloat result = tableView.rowHeight;
for (NSTableColumn *column in tableView.tableColumns) {
NSTextFieldCell *dataCell = column.dataCell;
if (![dataCell isKindOfClass:[NSTextFieldCell class]]) continue;
// Borrow the prototype cell, and set its text
[dataCell setObjectValue:[self stringValueForRow:row column:column]];
// Ask it the bounds for a rectangle as wide as the column
NSRect cellBounds = NSZeroRect;
cellBounds.size.width = [column width]; cellBounds.size.height = FLT_MAX;
NSSize cellSize = [dataCell cellSizeForBounds:cellBounds];
// This is a HACK to make this work.
// Otherwise the rows are inexplicably too short.
cellSize.height = cellSize.height + 0.5;
if (cellSize.height > result)
result = cellSize.height;
}
return result;
}
Just get -[NSTableView frame]
NSTableView is embed in NSScrollView, but has the full size.

How to highlight and edit the row (like iCal in SnowLeopard ) in NSTableView?

I would like to implement similar to iCal implementation. I want to highlight and edit the row at the same time. I have used cellBased tableview. I tried the following things.
[self.tableView editColumn:0 row:[self.array count]-1 withEvent:nil select:YES];
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:[self.array count]-1 ];
[self.tableView selectRowIndexes:indexSet byExtendingSelection:YES];
But above code highlights all the rows but not the current row. I want to highlight the current row and want to edit the same. Any hints to accomplish this?
I would like to implement some thing similar to Mail. When clicked on item,the row should get highlighted and the textField should get highlighted. I don't need an icon on the left side.
Customized the TableViewCell and implemented the drawWithFrame.
-(void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
{
NSRect newFrame = cellFrame;
newFrame.origin.x += 50;
newFrame.origin.y += 2;
newFrame.size.height -=4;
[super drawWithFrame:newFrame inView:controlView];
}
Following link helped me in resolving the issue.
http://developer.apple.com/library/mac/#samplecode/SourceView/Introduction/Intro.html

NSTableView + NSTextFieldCell dynamic row size

I want to display a string inside a tableview that can be ( max ) 4000 characters long. I have tried using NSAttributedString to find out the row height for the NSTextFieldCell, but I always fall a bit short when I insert > 4 lines of text.
The NSTextFieldCell is set to using NSLineBreakByWrapping for its line breaks.
What next to NSAttributedString can I use to get the height I need?
id cell = [[tableView tableColumnWithIdentifier:#"test"] dataCell];
[(NSTextFieldCell *)cell setStringValue:[_messages objectAtIndex:row]];
NSRect tallRect = NSMakeRect(0, 0, [tableView frame].size.width, CGFLOAT_MAX);
return [cell cellSizeForBounds:tallRect].height;