NSOutlineView на основе представления без NIB?

NSOutlineView это подкласс NSTableView, И в настоящее время NSTableView поддерживает две реализации.

  • Клеточная.
  • Просмотр основе.

Чтобы сделать боковую панель в стиле OSX 10.8 Finder (с автоматическим стилем серых значков), необходимо использовать табличное представление на основе вида со стилем выделения списка источников.

С NIB это типичная работа. Ничего сложного. (см. SidebarDemo) Но я хочу избежать любых NIB или Интерфейсного Разработчика. Я хочу сделать боковую панель чисто программно.

В этом случае у меня большая проблема. AFAIK, нет способа предоставить вид прототипа для конкретной ячейки. Когда я открою .xib файл, я вижу <tableColumn> содержит <prototypeCellViews>, И это указывает, какое представление будет использоваться для столбца. Я не могу найти, как установить это программно с помощью публичного API.

В качестве обходного пути я попытался сделать ячейку вручную, используя -[NSTableView makeViewWithIdentifier:owner:] а также -[NSTableView viewAtColumn:row:makeIfNecessary:], но ни один из них не возвращает представление экземпляра. Я создал NSTableCellView, но у него нет экземпляров вида изображения и текстового поля. И я также попытался установить их, но поля помечены как assign поэтому экземпляры немедленно освобождаются. Я пытался сохранить это, заставляя их удерживать, но это не работает. NSTableView не управляет ими, поэтому я уверен, что табличному представлению не нравится моя реализация.

Я считаю, что есть свойство для установки этого прототипа для столбца. Но я не могу их найти. Где я могу найти свойство и сделать системный дефолт NSOutlineView со стилем списка источников программно?

3 ответа

Решение

Если вы последуете примеру в SidebarDemoони используют подкласс NSTableCellView для подробных строк. Чтобы эмулировать moj InterfaceBuilder, вы можете соединить все вместе в конструкторе. Остальное так же, как демо (см. outlineView:viewForTableColumn:item:).

@interface SCTableCellView : NSTableCellView
@end

@implementation SCTableCellView

- (id)initWithFrame:(NSRect)frameRect {
  self = [super initWithFrame:frameRect];
  [self setAutoresizingMask:NSViewWidthSizable];
  NSImageView* iv = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 6, 16, 16)];
  NSTextField* tf = [[NSTextField alloc] initWithFrame:NSMakeRect(21, 6, 200, 14)];
  NSButton* btn = [[NSButton alloc] initWithFrame:NSMakeRect(0, 3, 16, 16)];
  [iv setImageScaling:NSImageScaleProportionallyUpOrDown];
  [iv setImageAlignment:NSImageAlignCenter];
  [tf setBordered:NO];
  [tf setDrawsBackground:NO];
  [[btn cell] setControlSize:NSSmallControlSize];
  [[btn cell] setBezelStyle:NSInlineBezelStyle];
  [[btn cell] setButtonType:NSMomentaryPushInButton];
  [[btn cell] setFont:[NSFont boldSystemFontOfSize:10]];
  [[btn cell] setAlignment:NSCenterTextAlignment];
  [self setImageView:iv];
  [self setTextField:tf];
  [self addSubview:iv];
  [self addSubview:tf];
  [self addSubview:btn];
  return self;
}

- (NSButton*)button {
  return [[self subviews] objectAtIndex:2];
}

- (void)viewWillDraw {
  [super viewWillDraw];
  NSButton* btn = [self button];
  ...

Вот код @jeberle, переписанный в Swift 4 (пять лет спустя!):

class ProgrammaticTableCellView: NSTableCellView {
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)

        self.autoresizingMask = .width
        let iv: NSImageView = NSImageView(frame: NSMakeRect(0, 6, 16, 16))
        let tf: NSTextField = NSTextField(frame: NSMakeRect(21, 6, 200, 14))
        let btn: NSButton = NSButton(frame: NSMakeRect(0, 3, 16, 16))
        iv.imageScaling = .scaleProportionallyUpOrDown
        iv.imageAlignment = .alignCenter
        tf.isBordered = false
        tf.drawsBackground = false
        btn.cell?.controlSize = .small
        // btn.bezelStyle = .inline                  // Deprecated?
        btn.cell?.isBezeled = true                   // Closest property I can find.
        // btn.cell?.setButtonType(.momentaryPushIn) // Deprecated?
        btn.setButtonType(.momentaryPushIn)
        btn.cell?.font = NSFont.boldSystemFont(ofSize: 10)
        btn.cell?.alignment = .center

        self.imageView = iv
        self.textField = tf
        self.addSubview(iv)
        self.addSubview(tf)
        self.addSubview(btn)
    }

    required init?(coder decoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    var button: NSButton {
        get {
            return self.subviews[2] as! NSButton
        }
    }
}

Изменить: я нашел ссылку (которая неизбежно сгниет - в последний раз она была изменена в 2011 году) на Apple SidebarDemo, на которой @jeberle основывал свой код.

В дополнение к ответу @jeberle мне нужно отметить кое-что еще.

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

  • Задавать NSTableView.rowSizeStyle в надлежащее значение (неCustom который является значением по умолчанию), чтобы сделать их табличное представление автоматически. В противном случае, вы должны макетировать их самостоятельно.

  • Не трогай frame а также autoresizing вещи, если вы хотите использовать предопределенные NSTableViewRowSizeStyle значение. В противном случае макет может быть нарушен.

  • Вы можете настроить высоту строки, предоставив private func outlineView(outlineView: NSOutlineView, heightOfRowByItem item: AnyObject) -> CGFloat метод делегата. настройка NSTableView.rowHeight не очень хорошая идея, потому что это нужно NSTableView.rowSizeStyle установлен в Custom который отключит управление текстовым / графическим макетом ячейки по умолчанию.

  • Вы можете повторно использовать представления строк / ячеек в настройках NSView.identifier имущество. ( пример)

Другие вопросы по тегам