Cocoa FAQ – часто задаваемые вопросы по Cocoa



Вопрос: Как снять выделение со всех элементов NSCollectionView?
Ответ:



Вопрос: Как показать контекстное меню для NSOutlineView?
Ответ:

Вопрос: Как программно выделить элемент в NSCollectionView?
Ответ:
setSelectionIndexes:

Вопрос: Как установить цвет рамки для NSView?
Ответ:
NSColor *orangeColor = [NSColor orangeColor];

// Convert to CGColorRef
NSInteger numberOfComponents = [orangeColor numberOfComponents];
CGFloat components[numberOfComponents];
CGColorSpaceRef colorSpace = [[orangeColor colorSpace] CGColorSpace];    
[orangeColor getComponents:(CGFloat *)&components];    
CGColorRef orangeCGColor = CGColorCreate(colorSpace, components);

// Set border
self.view.layer.borderColor = orangeCGColor;

// Clean up
CGColorRelease(orangeCGColor);

Также в 10.8+, можно использовать [aColor CGColor].

Вопрос: Как обработать смену выделения в NSOutlineView?
Ответ:
Для этого в протоколе NSOutlineViewDelegate есть методы:
  • outlineViewSelectionDidChange:
  • outlineViewSelectionIsChanging:

Вопрос: Как в NSCollectionView реализовать выделение элементов?
Ответ:
1) Включить в NSCollectionView опцию Selectable.
2) Переписать в наследнике класса NSCollectionViewItem (который естественно надо связать с Collection View Item объектом в xib файле) метод:
- (void)setSelected:(BOOL)selected
{
    [super setSelected:selected];
    if (selected)
        self.view.layer.backgroundColor = [NSColor redColor].CGColor;
    else
        self.view.layer.backgroundColor = [NSColor clearColor].CGColor;
}

Вопрос: Как создать NSColor заданием трех компонентов red, green, и blue?
Ответ:
// Вариант 1
NSColor *myColor = [NSColor colorWithCalibratedRed:redValue green:greenValue blue:blueValue alpha:1.0f];

// Вариант 2
float red = 0.5f;
float green = 0.2f;
float blue = 0.4f;
float alpha = 0.8f;
NSColor *rgb = [NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha];

// Вариант 3
[NSColor colorWithDeviceRed:0.886f green:0.886f blue:0.886f alpha:1.0f];

Вопрос: Как задать цвет текста для NSTextField?
Ответ:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    NSRect textFieldRect = NSMakeRect(300, 300, 300, 54);
    NSTextField* textField = [[NSTextField alloc] initWithFrame:textFieldRect];
    [textField setFont:[NSFont fontWithName:@"Arial" size:48]];
    [textField setTextColor:[NSColor whiteColor]];
    [textField setStringValue:@"Some Text"];
    [textField setBackgroundColor:[NSColor blackColor]];
    [textField setDrawsBackground:YES];
    [textField setBordered:NO];
    [[window contentView] addSubview:textField];
}

Вопрос: Как сделать рамку вокруг NSImageView?
Ответ:
[imgView setWantsLayer:YES];
imgView.layer.borderWidth = 1.0;
imgView.layer.cornerRadius = 8.0;
imgView.layer.masksToBounds = YES;
CGColorRef color = CGColorRetain([NSColor colorWithCalibratedRed:0 green:100 blue:0 alpha:0.5f].CGColor);
[imgView.layer setBorderColor:color];

Вопрос: Как получить выделенный элемент в NSOutlineView?
Ответ:
id selectedItem = [outlineView itemAtRow:[outlineView selectedRow]];

Вопрос: Как получить дочерний view по идентификатору или тэгу?
Ответ:
По идентификатору:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    NSView *viewToFind = [self viewWithIdentifier:@"54321"];
}  

- (NSView *)viewWithIdentifier:(NSString *)identifier
{
    NSArray *subviews = [self allSubviewsInView:self.window.contentView];

    for (NSView *view in subviews) {
        if ([view.identifier isEqualToString:identifier]) {
            return view; 
        }
    }

    return nil;
}

- (NSMutableArray *)allSubviewsInView:(NSView *)parentView {

    NSMutableArray *allSubviews     = [[NSMutableArray alloc] initWithObjects: nil];
    NSMutableArray *currentSubviews = [[NSMutableArray alloc] initWithObjects: parentView, nil];
    NSMutableArray *newSubviews     = [[NSMutableArray alloc] initWithObjects: parentView, nil];

    while (newSubviews.count) {
        [newSubviews removeAllObjects];

        for (NSView *view in currentSubviews) {
            for (NSView *subview in view.subviews) [newSubviews addObject:subview];
        }

        [currentSubviews removeAllObjects];
        [currentSubviews addObjectsFromArray:newSubviews];
        [allSubviews addObjectsFromArray:newSubviews];

    }

    for (NSView *view in allSubviews) {
        NSLog(@"View: %@, tag: %ld, identifier: %@", view, view.tag, view.identifier);
    }

    return allSubviews;
}


По тэгу:
// set the tag
NSInteger tagValue = 12345;
[self.myButton setTag:tagValue];

// find it 
NSButton *myButton = [self.window.contentView viewWithTag:12345];

Вопрос: Как получить путь к ресурсам Cocoa приложения?
Ответ:
NSBundle *theMainBundle = [NSBundle mainBundle];
NSString *theResourcePath = [theMainBundle resourcePath];
NSURL *theMyFolderURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/MyFolder", theResourcePath]];
NSFileManager *theFileManager = [[NSFileManager alloc] init];
NSArray *theMyFolderContents = [theFileManager contentsOfDirectoryAtURL: theMyFolderURL includingPropertiesForKeys:@[] options:NSDirectoryEnumerationSkipsHiddenFiles error:nil];


В переменной theMyFolderContents будет содержаться массив NSURL объектов представляющих собой содержимое папки MyFolder находящейся в ресурсах приложения.

Вопрос: В чем разница между строками возвращаемыми свойствами path и absoluteString класса NSURL?
Ответ:
absolute string: file:///Users/Devtype/Desktop/
path: /Users/Devtype/Desktop

Вопрос: Как считать файл в строку NSString?
Ответ:
NSURL *theNSURL = [NSURL fileURLWithPath:@"/Users/Devtype/Documents/data.txt"];
NSString *theNSString = [NSString stringWithContentsOfFile:theNSURL.path encoding:NSUTF8StringEncoding error:nil];

Объект класса NSURL также может быть определен как:
NSURL *theNSURL = [NSURL URLWithString:@"file:///Users/Devtype/Documents/data.txt"];

Вопрос: Как вынести пользовательский интерфейс в модуль?
Ответ:
Modular Cocoa User Interfaces | Intridea Blog

Вопрос: Как проверить NSString на пустоту?
Ответ:
static inline BOOL IsEmpty(id thing) {
return thing == nil
|| ([thing respondsToSelector:@selector(length)]
&& [(NSData *)thing length] == 0)
|| ([thing respondsToSelector:@selector(count)]
&& [(NSArray *)thing count] == 0);
}


Вопрос: Как отфильтровать массив строк?
Ответ:
NSArray* ar = [NSArray arrayWithObjects:@"on call", @"I'm on call", @"lala", @"On call", nil];
NSArray* filt = [ar filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self like[c] 'on call'"]];
NSLog([filt description]);

//Output
"on call",
"On call"

--

Вопрос: Как отфильтровать NSArrayController по типу объектов?
Ответ:

objective c - Problem filtering NSArrayController - Stack Overflow

Несколько вариантов:
  • [NSPredicate predicateWithFormat: @"className == %@", [someObject className]];
  • [NSPredicate predicateWithFormat: @"class == %@", [someObject class]];
  • NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", class];
  • NSPredicate* foo = [NSPredicate predicateWithFormat: @"className == %@", @"MyCustomView"];

objective c - Is it possible to filter an NSArray by class? - Stack Overflow:
NSArray *mixedArray = {...};
NSPredicate *predicate = [NSPredicate predicateWithFormat:
                                      @"self isKindOfClass: %@",
                                      [NSString class]];
NSLog(@"%@", [mixedArray filteredArrayUsingPredicate:predicate]);

Ещё вариант:
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
    return [object isKindOfClass:[NSString class]];
}];



Вопрос: Как преобразовать NSString в JSON строку?
Ответ:
-(NSString *)JSONString:(NSString *)aString {
 NSMutableString *s = [NSMutableString stringWithString:aString];
 [s replaceOccurrencesOfString:@"\"" withString:@"\\\"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
 [s replaceOccurrencesOfString:@"/" withString:@"\\/" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
 [s replaceOccurrencesOfString:@"\n" withString:@"\\n" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
 [s replaceOccurrencesOfString:@"\b" withString:@"\\b" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
 [s replaceOccurrencesOfString:@"\f" withString:@"\\f" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
 [s replaceOccurrencesOfString:@"\r" withString:@"\\r" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
 [s replaceOccurrencesOfString:@"\t" withString:@"\\t" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [s length])];
 return [NSString stringWithString:s];
}

Вопрос: Как определить MIME-тип файла?
Решение:
// To get the UTI from a MIME type.
NSURLResponse *response = ... // assume a URL response from somewhere else.
NSString *responseMIMEType = [response MIMEType];
CFStringRef MIMEType = (__bridge CFStringRef)[response MIMEType];
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, MIMEType, NULL);
NSString *UTIString = (__bridge_transfer NSString *)UTI;


// And to get the MIME type from the UTI.
NSString *filePath = ... // assume the path to a file from somewhere else.
CFStringRef fileExtension = (__bridge CFStringRef)[filePath pathExtension];
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, NULL);
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
CFRelease(UTI);
NSString *MIMETypeString = (__bridge_transfer NSString *)MIMEType;

Вопрос: Как преобразовать NSString в char* и обратно?
Решение:
// NSString to char*
NSString* nsstr = @"My NSString" ;
const char * cstr = [ nsstr cStringUsingEncoding:ASCIIEncoding ] ;
// There's also
const char * cstr2 = [ nsstr UTF8String ] ;

// char * to NSString
// Here you simply want to use one of the static method constructors
const char * cstyleString = "HELLO!!" ;
NSString * nsstr = [ NSString stringWithUTF8String:cstyleString ] ;
// Or an instance method 
NSString * nsstr2 = [[ NSString alloc ] initWithUTF8String:cstyleString ]
// sprintf() for NSString
[ [ NSString alloc ] initWithFormat:@"%s is %d years old", "Bobby", 45 ]

Вопрос: Как получить ключ по объекту в NSMutableDictionary?
Решение:
NSString *knownObject = @"the object";
NSArray *temp = [dict allKeysForObject:knownObject];
NSString *key = [temp objectAtIndex:0];
//"key" is now equal to the key of the object you were looking for

Вопрос: Как преобразовать NSImage в NSData?
Решение:
NSBitmapImageRep *imgRep = [[image representations] objectAtIndex: 0];
NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil];
[data writeToFile: @"/path/to/file.png" atomically: NO];
или:
- (NSData *) PNGRepresentationOfImage:(NSImage *) image {
// Create a bitmap representation from the current image
[image lockFocus];
NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, image.size.width, image.size.height)];
[image unlockFocus];
return [bitmapRep representationUsingType:NSPNGFileType properties:Nil];
}

Вопрос: Как преобразовать unsigned char массив в NSData и обратно?
Решение:
//
NSUInteger size = // some size
unsigned char array[size];
NSData* data = [NSData dataWithBytes:(const void *)array length:sizeof(unsigned char)*size];
//
NSUInteger size = [data length] / sizeof(unsigned char);
unsigned char* array = (unsigned char*) [NSData bytes];
//

Вопрос: Какие есть варианты для организации многопоточности?
Ответ:

Вопрос: Как приостановить поток?
Ответ:
NSCondition* condition; // initialize and release this is your app requires.

//Worker thread:
while([NSThread currentThread] isCancelled] == NO)
{
    [condition lock];
    while(partySuppliesAvailable == NO)
    {
        [condition wait];
    }

    // party!

    partySuppliesAvailable = NO;
    [condition unlock];
}

//Main thread:
[condition lock];
// Get party supplies
partySuppliesAvailable = YES;
[condition signal];
[condition unlock];
--

Вопрос: Как выполнить задачу в фоне и обновить UI?
Ответ:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
 //load your data here.
 dispatch_async(dispatch_get_main_queue(), ^{
                //update UI in main thread.
            });
});
--

Вопрос: Как вызвать selector у класса со статическим методом?
Ответ:
UITapGestureRecognizer * c1 = [[UITapGestureRecognizer alloc] 
      initWithTarget:[MyGestureRecognizer class]
      action:@selector(ViewWasClicked1:)];

Вопрос: Как остановить поток?
Ответ:
// At some checkpoint
if([[NSThread currentThread] isCancelled]) {
    /* do some clean up here */
    [NSThread exit];
}

Вопрос: Как удалить наблюдателя в NSNotificationCenter?
Ответ:
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"alert" object:nil];

Вопрос: Как создать индикатор выполнения процесса?
Ответ:

Вопрос: Как убрать header строку в NSOutlineView?
Ответ:
NSRect frame = _myOutlineView.headerView.frame;
frame.size.height = 0;
_myOutlineView.headerView.frame = frame;


Обратите внимание, что нельзя поменять сразу height, но можно поменять весь frame.

...