Как использовать автоматическое размещение с NSTableView на основе представления, когда представление предоставляется NSViewController?

Я сделал следующий пример приложения, чтобы проиллюстрировать свой вопрос.

  • Левое представление является представлением заполнителя (добавлено в Интерфейсном Разработчике). Когда приложение загружается, я добавляю подпредставление, управляемое NSViewController. NSViewController рисует прямоугольники разного цвета, каждый из которых является NSView, и макет этих цветных представлений управляется ограничением, созданным программно и добавленным к методу -loadView контроллера.

  • Правый вид - это NSTableView (добавлен в Interface Builder). Когда приложение загружается, я использую тот же класс NSViewController для предоставления представлений для представления таблицы (добавляется только одна строка).

Когда я добавляю подвид в представление заполнителя, я также добавляю два дополнительных ограничения:

[_placeholderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[CTRL_VIEW]|" options:0 metrics:nil views:views]];
[_placeholderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[CTRL_VIEW]|" options:0 metrics:nil views:views]];

Эти ограничения устанавливают фрейм подпредставления равными границам супервизора. Все хорошо.

Однако, когда я предоставляю представление для NSTableView с помощью метода делегата -tableView:viewForTableColumn:row:, представление еще не было добавлено в таблицу. Таким образом, у него нет супервизора, поэтому ограничения (пока) не могут быть добавлены в представление. Вот почему представление в табличном представлении не имеет тех же границ, что и у табличного представления.

Итак, мой вопрос: как я могу добавить ограничения к представлению, которое я предоставляю для табличного представления? Могу ли я снова получить доступ к представлению после того, как представление таблицы добавило его? Это кажется немного взломанным.

Два представления с использованием автолайпа.

Исходный код для AppDelegate.h,

#import <Cocoa/Cocoa.h>
@class BlahViewController;

@interface AppDelegate : NSObject <NSApplicationDelegate, NSTableViewDataSource, NSTableViewDelegate>

@property (assign) IBOutlet NSWindow *window;

/* Left view controller and place holding view */
@property (strong) BlahViewController *viewController;
@property (weak) IBOutlet NSView *placeholderView;

/* Right view (which is an NSTableView) */
@property (weak) IBOutlet NSTableView *tableView;


@end

и AppDelegate.m,

#import "AppDelegate.h"
#import "BlahViewController.h"

@interface AppDelegate ()
@property NSMutableArray *tableData;
@end

@implementation AppDelegate

- (id)init
{
    self = [super init];
    if (self) {

        _tableData = [[NSMutableArray alloc] init];
        [_tableData addObject:[[BlahViewController alloc] initWithNibName:@"BlahViewController" bundle:nil]];
        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints"];
    }
    return self;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    /* Add a managed subview to the place holder view*/
    _placeholderView.layer.backgroundColor = CGColorCreateGenericGray(0.5, 1.0);
    _viewController = [[BlahViewController alloc] initWithNibName:@"BlahViewController" bundle:nil];
    [_viewController.view setFrame:_placeholderView.bounds];
    [_placeholderView addSubview:_viewController.view];

    /* Additional constraints so the managed subview resizes with the place holder view */
    NSDictionary *views = @{ @"CTRL_VIEW" : _viewController.view };
    [_placeholderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[CTRL_VIEW]|" options:0 metrics:nil views:views]];
    [_placeholderView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[CTRL_VIEW]|" options:0 metrics:nil views:views]];

}

-(NSInteger) numberOfRowsInTableView:(NSTableView *)tableView
{
    return [_tableData count];
}

-(id) tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    return [_tableData[row] view];
}


-(CGFloat) tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row {
    return 150.;
}

@end

Обновите @jrturton

Добавление ограничений в -(void) tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row сработало.

С добавленными ограничениями.

@swalkner

Ограничения, которые я добавил, очень простые,

-(void) tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row {
NSView *view = [rowView viewAtColumn:0];
NSDictionary *views = NSDictionaryOfVariableBindings(view);
[view.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-12-[view]-12-|" options:0 metrics:nil views:views]];
[view.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-12-[view]-12-|" options:0 metrics:nil views:views]];
}

person Daniel Farrell    schedule 13.01.2013    source источник
comment
как твой tableView:didAddRowView:forRow: выглядит? Я бы хотел добиться того же, но не заставить это работать ...   -  person swalkner    schedule 09.07.2013
comment
Отмечая спец. Буквально просто примените ограничения к представлению (см. Выше, я сделал для вас правку).   -  person Daniel Farrell    schedule 09.07.2013


Ответы (2)


Вы можете получить доступ к представлению после того, как оно было добавлено в таблицу в методе делегата tableView:willDisplayCell:forTableColumn:row:. На этом этапе вы можете добавить ограничения, но вы можете быть осторожны, чтобы не добавлять одни и те же ограничения снова и снова.

Как вы сами выяснили, лучше использовать метод tableView:didAddRowView:forRow:, так как он будет вызываться только один раз для каждого нового представления.

Вы также можете добиться этого, установив маску автоматического изменения размера гибкой ширины в вашем представлении вместо любых ограничений по горизонтали. Таблица, вероятно, будет учитывать это при добавлении представлений ячеек. Если вы будете осторожны, вы можете смешивать и сопоставлять автоматическое изменение размера и ограничения.

Я знаю немного iOS, немного Autolayout и немного OS X, так что я не могу дать вам намного больше. Нам не нужно беспокоиться о том, что ширина ячеек сильно изменится в мире iOS!

person jrturton    schedule 13.01.2013
comment
Спасибо за совет из мира iOS :) Чем больше я узнаю об iOS, тем больше завидую. Похоже на гораздо более современный API. Я думаю, OSX догоняет. Новое в Lion Я нашел tableView: didAddRowView: forRow: в NSTableViewDelegate, следуя вашему совету. - person Daniel Farrell; 13.01.2013
comment
Это звучит идеально. Я обновлю свой ответ, включив это, если это сработало для вас? Таким образом, будущим читателям не придется оставлять комментарии. - person jrturton; 13.01.2013
comment
Да, это решило проблему, tableView: didAddRowView: forRow: кажется, вызывается после добавления представления, но до завершения цикла выполнения. Я добавил необходимые ограничения, и, похоже, это сработало. - person Daniel Farrell; 13.01.2013
comment
Я выдергивал из-за этого волосы какое-то время, просто смешно, что это не было должным образом задокументировано Apple. Это такой распространенный вариант использования. - person jbat100; 17.02.2013
comment
tableView:willDisplayCell:forTableColumn:row: вызывается для NSTableView на основе ячеек, поэтому это не лучшее решение для динамического изменения ограничений. - person Yoav; 13.03.2014
comment
Извините, я имел в виду, что он вызывается только для таблиц на основе ячеек и не вызывается для таблиц на основе представлений. - person Yoav; 13.03.2014

macOS 10.13, похоже, включает эту возможность из коробки с usesAutomaticRowHeights, но это еще не имеет объяснения в документах Какао:

https://developer.apple.com/documentation/appkit/nstableview/2870126-usesautomaticrowheights

Кроме того, это представлено в IB как параметр конфигурации для высоты просмотра ячейки.

Я не смог проверить, но, возможно, кто-то сможет.

person morpheby    schedule 06.09.2017