Jiv*_*Voe 82 macos cocoa objective-c osx-lion
我有一个基于视图的应用程序NSTableView
.在这个表视图中,我有一些行,这些行的单元格内容包含一个NSTextField
启用了自动换行的多行.根据文本内容,NSTextField
显示单元格所需的行大小会有所不同.
我知道我可以实现该NSTableViewDelegate
方法 - tableView:heightOfRow:
返回高度,但高度将根据上面使用的自动换行确定NSTextField
.这个词的包装NSTextField
同样基于NSTextField
它的宽度......这是由宽度决定的NSTableView
.
Soooo ......我想我的问题是......对于这个有什么好的设计模式?似乎我尝试的一切都变成了一个令人费解的混乱.因为TableView需要知道细胞的高度来展示它们......并且NSTextField
需要了解它的布局以确定自动换行...并且细胞需要知道自动换行来确定它的高度......这是一个圆形的混乱......它让我疯了.
建议?
如果重要,最终结果也将具有可编辑的NSTextFields
大小,以调整其中的文本.我已经在视图级别上工作了,但是tableview还没有调整单元格的高度.我想一旦我得到了高度问题,我将使用 - noteHeightOfRowsWithIndexesChanged
方法通知表格视图高度发生了变化......但是它仍然会向代表询问高度......因此,我的quandry.
提前致谢!
cor*_*unn 128
这是鸡和蛋的问题.该表需要知道行高,因为它决定了给定视图的位置.但是你想要一个视图已经存在,所以你可以用它来计算行高.那么,哪个先来?
答案是NSTableCellView
为了测量视图的高度而保留额外的(或者您正在使用的任何视图作为"单元视图").在tableView:heightOfRow:
委托方法中,访问"row"的模型并将其设置为objectValue
on NSTableCellView
.然后将视图的宽度设置为表格的宽度,然后(但是你想要这样做)计算出该视图所需的高度.返回那个值.
不要noteHeightOfRowsWithIndexesChanged:
在委托方法中调用tableView:heightOfRow:
或者viewForTableColumn:row:
!这很糟糕,会造成巨大的麻烦.
要动态更新高度,那么您应该做的是响应文本更改(通过目标/操作)并重新计算该视图的计算高度.现在,不要动态更改NSTableCellView
高度(或者您正在使用的任何视图作为"单元格视图").该表必须控制该视图的框架,如果您尝试设置它,您将对抗tableview.相反,在计算高度的文本字段的目标/操作中,调用 noteHeightOfRowsWithIndexesChanged:
,这将使表调整该行的大小.假设您在子视图(即:子视图NSTableCellView
)上设置了自动调整遮罩设置,那么事情应该调整好!如果没有,首先处理子视图的大小调整掩码,以使变量行高度正确.
noteHeightOfRowsWithIndexesChanged:
默认情况下不要忘记动画.为了使它不具有动画效果:
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0];
[tableView noteHeightOfRowsWithIndexesChanged:indexSet];
[NSAnimationContext endGrouping];
Run Code Online (Sandbox Code Playgroud)
PS:我对Apple Dev Forums上发布的问题的反应比堆栈溢出更多.
PSS:我写了基于NSTableView的视图
Cli*_*rum 29
这得到了很多容易的MacOS 10.13用.usesAutomaticRowHeights
.详细信息如下:https://developer.apple.com/library/content/releasenotes/AppKit/RN-AppKit/#10_13(在标题为"NSTableView自动行高"的部分中).
基本上你只需选择你的NSTableView
或NSOutlineView
在故事板编辑器中并在尺寸检查器中选择此选项:
然后,您将设置的内容设置为NSTableCellView
单元格的顶部和底部约束,并且您的单元格将调整大小以自动适应.无需代码!
您的应用将忽略heightOfRow
(NSTableView
)和heightOfRowByItem
(NSOutlineView
)中指定的任何高度.您可以使用以下方法查看为自动布局行计算的高度:
func outlineView(_ outlineView: NSOutlineView, didAdd rowView: NSTableRowView, forRow row: Int) {
print(rowView.fittingSize.height)
}
Run Code Online (Sandbox Code Playgroud)
根据Corbin的回答(顺便说一下,对此有所了解):
Swift 3,基于视图的NSTableView,具有自动布局,适用于macOS 10.11(及更高版本)
我的设置:我有一个NSTableCellView
使用自动布局布局.它包含(除了其他元素之外)多行NSTextField
,最多可包含2行.因此,整个单元格视图的高度取决于此文本字段的高度.
我更新告诉表视图两次更新高度:
1)当表视图调整大小时:
func tableViewColumnDidResize(_ notification: Notification) {
let allIndexes = IndexSet(integersIn: 0..<tableView.numberOfRows)
tableView.noteHeightOfRows(withIndexesChanged: allIndexes)
}
Run Code Online (Sandbox Code Playgroud)
2)当数据模型对象发生变化时:
tableView.noteHeightOfRows(withIndexesChanged: changedIndexes)
Run Code Online (Sandbox Code Playgroud)
这将导致表视图询问它的委托是否有新的行高.
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
}
Run Code Online (Sandbox Code Playgroud)
首先,我们需要获取row(entity
)的模型对象和相应的单元视图标识符.然后,我们检查是否已经为此标识符创建了一个视图.为此,我们必须为每个标识符维护一个包含单元格视图的列表:
// We need to keep one cell view (per identifier) around
fileprivate var savedTableCellViews = [String : NSTableCellView]()
Run Code Online (Sandbox Code Playgroud)
如果没有保存,我们需要创建(并缓存)一个新的.我们使用模型对象更新单元格视图,并告诉它根据当前表格视图宽度重新布局所有内容.fittingSize
然后可以将高度用作新高度.
对于想要更多代码的人来说,这是我使用的完整解决方案.谢谢corbin dunn指出我正确的方向.
我需要在关系大多设定的高度有多高一NSTextView
的我NSTableViewCell
了.
在我的子类中,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;
}
Run Code Online (Sandbox Code Playgroud)
在我的IBAnnotationTableViewCell
哪个是我的单元格的控制器(子类NSTableCellView
)我有一个设置方法
-(void)setupWithPDFAnnotation:(PDFAnnotation*)annotation;
Run Code Online (Sandbox Code Playgroud)
它设置所有出口并设置我的PDFAnnotations中的文本.现在我可以使用以下方法"轻松"加工高度:
-(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;
}
Run Code Online (Sandbox Code Playgroud)
.
- (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 ;
}
Run Code Online (Sandbox Code Playgroud)
.
- (float)heightForWidth:(float)width forString:(NSAttributedString*)string
{
return [self sizeForWidth:width height:FLT_MAX forString:string].height ;
}
Run Code Online (Sandbox Code Playgroud)