2011-12-28 6 views
31

NSCollectionView은 내가 본 코코아 API 중 가장 신비한 부분 중 하나입니다. 문서가 불량하고 움직이는 부분이 많습니다. 많은 부분이 Interface Builder에서 구현되기 때문에 문서 작성이 까다로울 수 있습니다.처음부터 프로그래밍 방식으로 NSCollectionView를 만드는 방법은 무엇입니까?

NSCollectionView의 간단한 케이스를 만들기 위해 샘플 코드를 제공하십시오.이 코드는 각 텍스트 필드 또는 버튼의 제목이 다른 Xcode를 사용하지 않고 텍스트 필드 또는 버튼을 표시합니다. 기본 window IBOutlet을 가진 새로운 Xcode 프로젝트를 가정하십시오.

이 예제의 경우 데이터 소스가 변경 될 때 NSCollectionView를 업데이트하기 위해 바인딩이 필요하지 않습니다. 프로토 타입 객체 그리드를 표시하고 각 객체의 Title을 일부 값으로 설정하기 만하면됩니다.

우리가 이것을 많은 사람들에게 제공하는 좋은 방법을 얻을 수 있다면, 나는 NSCollectionViews과 함께 일하는 모든 사람들을 도우며 내가 당황하는 것처럼 생각합니다. 요청의

요약

  • 새로운 Xcode 프로젝트에 NSCollectionView을 렌더링하는 샘플 코드를 제공
  • 함께 IBOutlet는
  • NSCollectionView가 텍스트를 포함해야합니다 제공하는 기본 창을 사용하여 수행 인터페이스 빌더를 사용하지 마십시오 필드 또는 버튼을 선택하십시오.
  • 보기의 각 항목은 다른 제목을 가져야합니다.
  • 바인드 없음 g가 필요합니다.

이러한 요구 사항을 충족하는 샘플 코드가 있다면 위 링크를 제공해주세요.

답변

59

프로그래밍 방식으로 그리고 바인딩없이 컬렉션 뷰를 만드는 데 많은 통찰력이 있는지는 모르겠습니다 만 여기서는 간략히 설명합니다.

  • 보기 : 네 가지 구성 요소 콜렉션 뷰 사용하여 기본적으로 있습니다 소개

    정보를 표시 할 책임이 NSView의 서브 클래스;

  • 컬렉션보기 자체.
  • 보기 컨트롤러 : 컬렉션보기 항목 프로토 타입 역할을하는 NSCollectionViewItem의 하위 클래스.
  • 모델 : 개체의 배열입니다.

일반적으로보기는 Interface Builder에서 설계되었으며 모델은 Cocoa 바인딩에 의해 조정됩니다.

프로그래밍을 수행 :

상수

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 

보기 컨트롤러 (프로토 타입)

는 일반적으로 뷰 컨트롤러는 nib 파일에서 해당 뷰를로드합니다. 보기 컨트롤러가 nib 파일에서 해당보기를 가져 오지 않는 드문 경우에 개발자는 -setView:을 보내고 -view을보기 컨트롤러가 수신하거나 -loadView을 무시해야합니다. 다음 코드는 후자를 수행합니다.

View 컨트롤러는 -setRepresentedObject:을 통해 해당 모델 객체를받습니다. 모델 객체가 변경 될 때마다 버튼 제목을 업데이트하기 위해이를 오버라이드했습니다. 이것은 코드없이 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 

모델

문자열을 나타내는 버튼 타이틀의 간단한 배열 :

@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 
+15

NSCollectionView에 관한 문서는 놀랍지 않게 잘 못됩니다. 이 신화적인 짐승의 작동 방식을 이해하고 지식을 공유 할 수있는 사람이라면 누구나 어디서나 Objective-C 개발자를 도울 수 있습니다. 감사합니다. – Tronathan

+0

Bavarious, 변경할 수있는 배열로 컬렉션보기를 동기화하는 방법을 보여줄 수 있습니까? – brigadir

+0

@Bavarious - 좋은 답변에 감사드립니다. –

4

에 대한 변경 가능한 배열에 바인딩하는 방법에 대한 brigadir의 질문에 대답합니다.

0 번째 - 제목이 NSMutableArray

먼저 확인 - 항목

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

둘째로 배열을 바인딩 - 제목을 변경하는 경우, 프록시를 수정해야합니다.

@Bavarious

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

당신이 훌륭한 일을했다. 이것은 제가 Apple Docs에서 때때로 누락하는 놀라운 튜토리얼이었습니다.

// AppDelegate.swift :

내가 관심있는 사람을 위해 스위프트 (V2)에 Bavarious '코드를 재 작성

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) 
    } 
} 
관련 문제