Подтвердить что ты не робот

Как создать программный код NSCollectionView с нуля?

NSCollectionView остается одной из самых загадочных частей API Cocoa, которые я когда-либо видел. Документация плохая, и есть много движущихся частей, многие из которых часто реализуются в Interface Builder, что затрудняет документацию.

Просьба представить пример кода для создания простейшего случая NSCollectionView, который отображает текстовые поля или кнопки без использования Xcode, где каждое текстовое поле или кнопка имеет другое название. Предположим, что новый проект Xcode с по умолчанию window IBOutlet.

В этом примере привязка не требуется для обновления NSCollectionView при изменении источника данных. Просто покажите сетку объектов-прототипов и установите для каждого объекта Title значение.

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

Сводка запроса

  • Предоставить пример кода для рендеринга NSCollectionView в новом проекте Xcode
  • Не используйте Interface Builder, используйте окно по умолчанию, которое предоставляется IBOutlet.
  • NSCollectionView должен содержать текстовые поля или кнопки, ваш выбор
  • Каждый элемент в представлении должен иметь другое название
  • Не требуется привязка

Если есть пример кода, который соответствует этим требованиям, укажите ссылку, это было бы здорово!

4b9b3361

Ответ 1

Я не уверен, что вы много понимаете при создании коллективного представления программно и без привязок, но здесь оно идет.

Введение

При использовании представления коллекции по существу четыре компонента:

  • Вид: подкласс NSView, ответственный за отображение информации;
  • сам просмотр коллекции;
  • Просмотреть контроллер: подкласс NSCollectionViewItem, который служит прототипом элемента представления коллекции;
  • Модель: массив объектов.

Обычно представление создается в Interface Builder, а модель поддерживается Cocoa привязками.

Выполнение программно:

Константы

static const NSSize buttonSize = {80, 20};
static const NSSize itemSize = {100, 40};
static const NSPoint buttonOrigin = {10, 10};

Просмотр

Это стандартное представление (пользовательский вид в языке Interface Builder), содержащий кнопку. Обратите внимание, что представление имеет фиксированный размер.

@interface BVView : NSView
@property (weak) NSButton *button;
@end

@implementation BVView
@synthesize button;
- (id)initWithFrame:(NSRect)frameRect {
    self = [super initWithFrame:(NSRect){frameRect.origin, itemSize}];
    if (self) {
        NSButton *newButton = [[NSButton alloc] 
            initWithFrame:(NSRect){buttonOrigin, buttonSize}];
        [self addSubview:newButton];
        self.button = newButton;
    }
    return self;
}
@end

Просмотреть контроллер (прототип)

Обычно контроллер представления загружает свой вид из файла nib. В редких случаях, когда контроллер представления не получает свой вид из файла nib, разработчик должен либо отправить его -setView:, прежде чем -view будет получен контроллером представления, либо переопределит -loadView. Следующий код делает последний.

Контроллеры просмотра получают соответствующий объект модели через -setRepresentedObject:. Ive переопределил его, чтобы обновить заголовок кнопки всякий раз, когда изменяется объект модели. Обратите внимание, что это может быть выполнено с помощью Cocoa привязок без кода вообще.

Обратите внимание, что ни один из этих кодов не относится к представлениям коллекций - его общее поведение контроллера представления.

@interface BVPrototype : NSCollectionViewItem
@end

@implementation BVPrototype
- (void)loadView {
    [self setView:[[BVView alloc] initWithFrame:NSZeroRect]];
}
- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];
    [[(BVView *)[self view] button] setTitle:representedObject];
}
@end

Model

Простой массив строк, обозначающих названия кнопок:

@property (strong) NSArray *titles;
self.titles = [NSArray arrayWithObjects:@"Case", @"Molly", @"Armitage",
    @"Hideo", @"The Finn", @"Maelcum", @"Wintermute", @"Neuromancer", nil];

Вид коллекции

До сих пор единственное отношение, которое было установлено, - это вид (BVView), используемый прототипом элемента (BVPrototype). Представление коллекции должно быть проинформировано о прототипе, который он должен использовать, а также о модели, из которой можно получить данные.

NSCollectionView *cv = [[NSCollectionView alloc]
    initWithFrame:[[[self window] contentView] frame]]; 
[cv setItemPrototype:[BVPrototype new]];
[cv setContent:[self titles]];

Полный исходный код для делегата приложения

#import "BVAppDelegate.h"


static const NSSize buttonSize = { 80, 20 };
static const NSSize itemSize = { 100, 40 };
static const NSPoint buttonOrigin = { 10, 10 };


@interface BVView : NSView
@property (weak) NSButton *button;
@end

@implementation BVView
@synthesize button;
- (id)initWithFrame:(NSRect)frameRect {
    self = [super initWithFrame:(NSRect){frameRect.origin, itemSize}];
    if (self) {
        NSButton *newButton = [[NSButton alloc]
            initWithFrame:(NSRect){buttonOrigin, buttonSize}];
        [self addSubview:newButton];
        self.button = newButton;
    }
    return self;
}
@end


@interface BVPrototype : NSCollectionViewItem
@end

@implementation BVPrototype
- (void)loadView {
    [self setView:[[BVView alloc] initWithFrame:NSZeroRect]];
}
- (void)setRepresentedObject:(id)representedObject {
    [super setRepresentedObject:representedObject];
    [[(BVView *)[self view] button] setTitle:representedObject];
}
@end


@interface BVAppDelegate ()
@property (strong) NSArray *titles;
@end

@implementation BVAppDelegate

@synthesize window = _window;
@synthesize titles;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    self.titles = [NSArray arrayWithObjects:@"Case", @"Molly", @"Armitage",
        @"Hideo", @"The Finn", @"Maelcum", @"Wintermute", @"Neuromancer", nil];

    NSCollectionView *cv = [[NSCollectionView alloc]
        initWithFrame:[[[self window] contentView] frame]]; 
    [cv setItemPrototype:[BVPrototype new]];
    [cv setContent:[self titles]];

    [cv setAutoresizingMask:(NSViewMinXMargin
                             | NSViewWidthSizable
                             | NSViewMaxXMargin
                             | NSViewMinYMargin
                             | NSViewHeightSizable
                             | NSViewMaxYMargin)];
    [[[self window] contentView] addSubview:cv];
}

@end

Ответ 2

@Bavarious Там вы отлично поработали. Это был просто потрясающий учебник, который я иногда пропускаю в Документах Apple.

Я переписал код Bavarious в Swift (v2) для всех, кто интересуется:

//AppDelegate.swift:

import Cocoa

let buttonSize:NSSize = NSSize(width: 80, height: 20)
let itemSize:NSSize = NSSize(width: 100, height: 40)
let buttonOrigin:NSPoint = NSPoint(x: 10, y: 10)

let titles:[String] = ["Case", "Molly", "Armitage", "Hideo", "The Finn", "Maelcum", "Wintermute", "Neuromancer"]

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    @IBOutlet weak var window: NSWindow!

    func applicationDidFinishLaunching(aNotification: NSNotification) {
        let cv = NSCollectionView(frame: self.window.contentView!.frame)
        cv.itemPrototype = BVTemplate()
        cv.content = titles

        cv.autoresizingMask = NSAutoresizingMaskOptions.ViewMinXMargin
            .union(NSAutoresizingMaskOptions.ViewWidthSizable)
            .union(NSAutoresizingMaskOptions.ViewMaxXMargin)
            .union(NSAutoresizingMaskOptions.ViewMinYMargin)
            .union(NSAutoresizingMaskOptions.ViewMaxYMargin)
            .union(NSAutoresizingMaskOptions.ViewHeightSizable)

        window.contentView!.addSubview(cv)
    }

    func applicationWillTerminate(aNotification: NSNotification) {
        // Insert code here to tear down your application
    }
}

//BVTemplate.swift:

import Cocoa

class BVTemplate: NSCollectionViewItem {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
    }

    override func loadView() {
        print("loadingView")
        self.view = BVView(frame: NSZeroRect)
    }

    override var representedObject:AnyObject? {
        didSet {
            if let representedString = representedObject as? String {
                (self.view as! BVView).button?.title = representedString
            }
        }
    }
}

//BVView.swift:

import Cocoa

class BVView: NSView {

    var button:NSButton?

    override init(frame frameRect: NSRect) {
        super.init(frame: NSRect(origin: frameRect.origin, size: itemSize))
        let newButton:NSButton = NSButton(frame: NSRect(origin: buttonOrigin, size: buttonSize))
        self.addSubview(newButton)
        self.button = newButton
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
}

Ответ 3

Чтобы ответить на вопрос бригадира о том, как привязать к изменяемому массиву.

zero'th - создавать заголовки NSMutableArray

сначала - привяжите массив к вашим элементам

[cv bind:NSContentBinding 
    toObject:self 
    withKeyPath:@"titles" 
    options:NULL];

Вторые - при изменении заголовков обязательно измените прокси.

например.

NSMutableArray *kvcTitles = [self mutableArrayValueForKey:@"titles"];
[kvcTitles removeLastObject];