Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/macos/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 具有动态高度行的基于视图的NSTableView_Objective C_Macos_Cocoa_Osx Lion - Fatal编程技术网

Objective c 具有动态高度行的基于视图的NSTableView

Objective c 具有动态高度行的基于视图的NSTableView,objective-c,macos,cocoa,osx-lion,Objective C,Macos,Cocoa,Osx Lion,我有一个应用程序,其中包含一个基于视图的NSTableView。在这个表视图中,我有一些行,这些行的单元格的内容由启用换行的多行NSTextField组成。根据NSTextField的文本内容,显示单元格所需的行大小将有所不同 我知道我可以实现NSTableViewDelegate方法-tableView:heightOfRow:来返回高度,但高度将根据NSTextField上使用的换行字来确定。NSTextField的换行字符类似地基于NSTextField的宽度…这是由NSTableView

我有一个应用程序,其中包含一个基于视图的
NSTableView
。在这个表视图中,我有一些行,这些行的单元格的内容由启用换行的多行
NSTextField
组成。根据
NSTextField
的文本内容,显示单元格所需的行大小将有所不同

我知道我可以实现
NSTableViewDelegate
方法-
tableView:heightOfRow:
来返回高度,但高度将根据
NSTextField
上使用的换行字来确定。
NSTextField
的换行字符类似地基于
NSTextField
的宽度…这是由
NSTableView
的宽度决定的

Soooo…我想我的问题是…什么是好的设计模式?看来我尝试的每件事都是一团糟。由于TableView需要了解单元格的高度才能进行布局。。。而
NSTextField
需要知道它的布局来确定换行符…单元格需要知道换行符来确定它的高度…这是一个圆形的混乱…这让我发疯

建议

如果重要的话,最终结果还将具有可编辑的
NSTextFields
,这些字段将调整大小以适应其中的文本。我已经在视图级别进行了此操作,但tableview尚未调整单元格的高度。我想,一旦我解决了高度问题,我将使用-
notehightofrowswithindexchanged
方法通知表视图高度已更改…但它仍然会向学员询问高度…因此,我的quandry


提前谢谢

你看过吗?它已经很旧了,我还没有测试过它,但它仍然可以工作。

这听起来很像我以前不得不做的事情。我希望我能告诉你,我想出了一个简单、优雅的解决方案,但是,唉,我没有。但不是因为缺乏尝试。正如您已经注意到的,UITableView需要在构建单元之前知道高度,这使得它看起来非常圆

我的最佳解决方案是将逻辑推送到单元,因为至少我可以分离出理解单元布局所需的类。像这样的方法

+ (CGFloat) heightForStory:(Story*) story

能够确定细胞的高度。当然,这涉及到测量文本等。在某些情况下,我设计了一些方法来缓存在此方法中获得的信息,这些信息可以在创建单元格时使用。这是我想出的最好的办法。这是一个令人恼火的问题,但似乎应该有更好的答案。

这是一个鸡和蛋的问题。表格需要知道行高,因为行高决定了给定视图的位置。但您希望视图已经存在,以便可以使用它计算行高。那么,哪个先来


答案是保留一个额外的
NSTableCellView
(或任何用作“单元视图”的视图),仅用于测量视图的高度。在
tableView:heightOfRow:
delegate方法中,访问“row”的模型,并在
NSTableCellView上设置
objectValue
。然后将视图的宽度设置为表的宽度,然后(不管您想怎么做)计算出该视图所需的高度。返回该值

不要在委托方法中调用
notehightofrowswithindexchanged:
“表视图:heightOfRow:
viewfortable列:行:
”!这是不好的,而且会造成巨大的麻烦

要动态更新高度,则应响应文本更改(通过目标/操作)并重新计算该视图的计算高度。现在,不要动态更改
NSTableCellView
的高度(或任何用作“单元视图”的视图)。表必须控制该视图的框架,如果试图设置它,您将与表视图发生冲突。相反,在计算高度的文本字段的目标/操作中,调用
notehightofrowswithindexchanged:
,这将允许表格调整该行的大小。假设您在子视图(即
NSTableCellView
的子视图)上设置了自动调整大小的掩码,则应该可以调整大小!如果没有,请首先使用子视图的调整大小掩码,以正确处理可变行高

不要忘记
notehightofrowswithindexchanged:
默认设置动画。要使其不具有动画效果,请执行以下操作:

[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0];
[tableView noteHeightOfRowsWithIndexesChanged:indexSet];
[NSAnimationContext endGrouping];
附言:我对苹果开发者论坛上发布的问题的回应要多于堆栈溢出


PSS:我编写了基于视图的NSTableView

以下是我所做的修复工作:

来源:查看XCode文档,在“行高nstableview”下。您将找到一个名为“TableViewVariableRowHeights/TableViewVariableRowHeightsAppDelegate.m”的示例源代码

(注意:我正在查看表视图中的第1列,您必须调整以查看其他位置)

在Delegate.h中

IBOutlet NSTableView            *ideaTableView;
在Delegate.m中

表视图代理行高度的控制

    - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row {
    // Grab the fully prepared cell with our content filled in. Note that in IB the cell's Layout is set to Wraps.
    NSCell *cell = [ideaTableView preparedCellAtColumn:1 row:row];

    // See how tall it naturally would want to be if given a restricted with, but unbound height
    CGFloat theWidth = [[[ideaTableView tableColumns] objectAtIndex:1] width];
    NSRect constrainedBounds = NSMakeRect(0, 0, theWidth, CGFLOAT_MAX);
    NSSize naturalSize = [cell cellSizeForBounds:constrainedBounds];

    // compute and return row height
    CGFloat result;
    // Make sure we have a minimum height -- use the table's set height as the minimum.
    if (naturalSize.height > [ideaTableView rowHeight]) {
        result = naturalSize.height;
    } else {
        result = [ideaTableView rowHeight];
    }
    return result;
}
您还需要这一点来影响新行高(委托方法)

我希望这有帮助


最后一点注意:这不支持更改列宽。

对于想要更多代码的人,这里是我使用的完整解决方案。感谢科尔宾·邓恩为我指明了正确的方向

我需要设置的高度主要与我的
NSTableViewCell
中的
NSTextView
的高度有关

在我的
NSViewController
子类中,我通过调用
outlineView:viewForTableColumn:item:

- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
{
    NSTableColumn *tabCol = [[outlineView tableColumns] objectAtIndex:0];
    IBAnnotationTableViewCell *tableViewCell = (IBAnnotationTableViewCell*)[self outlineView:outlineView viewForTableColumn:tabCol item:item];
    float height = [tableViewCell getHeightOfCell];
    return height;
}

- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
    IBAnnotationTableViewCell *tableViewCell = [outlineView makeViewWithIdentifier:@"AnnotationTableViewCell" owner:self];
    PDFAnnotation *annotation = (PDFAnnotation *)item;
    [tableViewCell setupWithPDFAnnotation:annotation];
    return tableViewCell;
}
在我的
IBAnnotationTableViewCell
中,我有一个设置方法,它是我的单元格的控制器(
NSTableCellView
的子类)

-(void)setupWithPDFAnnotation:(PDFAnnotation*)annotation;
whi
-(void)setupWithPDFAnnotation:(PDFAnnotation*)annotation;
-(float)getHeightOfCell
{
    return [self getHeightOfContentTextView] + 60;
}

-(float)getHeightOfContentTextView
{
    NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[self.contentTextView font],NSFontAttributeName,nil];
    NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[self.contentTextView string] attributes:attributes];
    CGFloat height = [self heightForWidth: [self.contentTextView frame].size.width forString:attributedString];
    return height;
}
- (NSSize)sizeForWidth:(float)width height:(float)height forString:(NSAttributedString*)string
{
    NSInteger gNSStringGeometricsTypesetterBehavior = NSTypesetterLatestBehavior ;
    NSSize answer = NSZeroSize ;
    if ([string length] > 0) {
        // Checking for empty string is necessary since Layout Manager will give the nominal
        // height of one line if length is 0.  Our API specifies 0.0 for an empty string.
        NSSize size = NSMakeSize(width, height) ;
        NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:size] ;
        NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:string] ;
        NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init] ;
        [layoutManager addTextContainer:textContainer] ;
        [textStorage addLayoutManager:layoutManager] ;
        [layoutManager setHyphenationFactor:0.0] ;
        if (gNSStringGeometricsTypesetterBehavior != NSTypesetterLatestBehavior) {
            [layoutManager setTypesetterBehavior:gNSStringGeometricsTypesetterBehavior] ;
        }
        // NSLayoutManager is lazy, so we need the following kludge to force layout:
        [layoutManager glyphRangeForTextContainer:textContainer] ;

        answer = [layoutManager usedRectForTextContainer:textContainer].size ;

        // Adjust if there is extra height for the cursor
        NSSize extraLineSize = [layoutManager extraLineFragmentRect].size ;
        if (extraLineSize.height > 0) {
            answer.height -= extraLineSize.height ;
        }

        // In case we changed it above, set typesetterBehavior back
        // to the default value.
        gNSStringGeometricsTypesetterBehavior = NSTypesetterLatestBehavior ;
    }

    return answer ;
}
- (float)heightForWidth:(float)width forString:(NSAttributedString*)string
{
    return [self sizeForWidth:width height:FLT_MAX forString:string].height ;
}
- (double)tableView:(NSTableView *)tableView heightOfRow:(long)row
{
    if (tableView == self.tableViewTodo)
    {
        CKRecord *record = [self.arrayTodoItemsFiltered objectAtIndex:row];
        NSString *text = record[@"title"];

        double someWidth = self.tableViewTodo.frame.size.width;
        NSFont *font = [NSFont fontWithName:@"Palatino-Roman" size:13.0];
        NSDictionary *attrsDictionary =
        [NSDictionary dictionaryWithObject:font
                                    forKey:NSFontAttributeName];
        NSAttributedString *attrString =
        [[NSAttributedString alloc] initWithString:text
                                        attributes:attrsDictionary];

        NSRect frame = NSMakeRect(0, 0, someWidth, MAXFLOAT);
        NSTextView *tv = [[NSTextView alloc] initWithFrame:frame];
        [[tv textStorage] setAttributedString:attrString];
        [tv setHorizontallyResizable:NO];
        [tv sizeToFit];

        double height = tv.frame.size.height + 20;

        return height;
    }

    else
    {
        return 18;
    }
}
@implementation NSTextField (IDDAppKit)

- (CGFloat)heightForWidth:(CGFloat)width {
    CGSize size = NSMakeSize(width, 0);
    NSFont*  font = self.font;
    NSDictionary*  attributesDictionary = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
    NSRect bounds = [self.stringValue boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:attributesDictionary];

    return bounds.size.height;
}

@end
func tableViewColumnDidResize(_ notification: Notification) {
        let allIndexes = IndexSet(integersIn: 0..<tableView.numberOfRows)
        tableView.noteHeightOfRows(withIndexesChanged: allIndexes)
}
tableView.noteHeightOfRows(withIndexesChanged: changedIndexes)
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {

    // Get data object for this row
    let entity = dataChangesController.entities[row]

    // Receive the appropriate cell identifier for your model object
    let cellViewIdentifier = tableCellViewIdentifier(for: entity)

    // We use an implicitly unwrapped optional to crash if we can't create a new cell view
    var cellView: NSTableCellView!

    // Check if we already have a cell view for this identifier
    if let savedView = savedTableCellViews[cellViewIdentifier] {
        cellView = savedView
    }
    // If not, create and cache one
    else if let view = tableView.make(withIdentifier: cellViewIdentifier, owner: nil) as? NSTableCellView {
        savedTableCellViews[cellViewIdentifier] = view
        cellView = view
    }

    // Set data object
    if let entityHandler = cellView as? DataEntityHandler {
        entityHandler.update(with: entity)
    }

    // Layout
    cellView.bounds.size.width = tableView.bounds.size.width
    cellView.needsLayout = true
    cellView.layoutSubtreeIfNeeded()

    let height = cellView.fittingSize.height

    // Make sure we return at least the table view height
    return height > tableView.rowHeight ? height : tableView.rowHeight
}
// We need to keep one cell view (per identifier) around
fileprivate var savedTableCellViews = [String : NSTableCellView]()
var tableViewCellForSizing: NSTableCellView?
tableViewCellForSizing = tableView.make(withIdentifier: "My Identifier", owner: self) as? NSTableCellView
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
    guard let tableCellView = tableViewCellForSizing else { return minimumCellHeight }

    tableCellView.textField?.attributedStringValue = attributedString[row]
    if let height = tableCellView.textField?.fittingSize.height, height > 0 {
        return height
    }

    return minimumCellHeight
}
func outlineView(_ outlineView: NSOutlineView, didAdd rowView: NSTableRowView, forRow row: Int) {
  print(rowView.fittingSize.height)
}