Пример таблицы с двумя столбцами y = mx пример - PullRequest
0 голосов
/ 15 декабря 2010

Как бы я сделал табличное представление с 2 столбцами (x и y) для функции y = mx

Я перепробовал много вещей, которые заканчиваются полным провалом.

Может кто-нибудь сделать мне пример кода и объяснить его.

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

У меня есть это

 //array.m

    #import "array.h"

    @implementation array

    - (IBAction)makeArrays:(id)sender 
{
    int x,y;
    NSNumber *multiplier=[NSNumber numberWithFloat:[mField floatValue]];

    for (x=0;x++;x<181)
    {
        y=[multiplier floatValue]*x;

        NSNumber *xValue = [NSNumber numberWithInt:x];
        NSNumber *yValue = [NSNumber numberWithInt:x];

    NSArray  *xArray = [NSArray arrayWithObject:xValue];
    NSArray  *yArray = [NSArray arrayWithObject:yValue];
    }
}

@end

и файл класса

//array.h

#import <Cocoa/Cocoa.h>

@interface array : NSObject {
IBOutlet id mField; 
}
- (IBAction)makeArrays:(id)sender;

@end

куда мне идти отсюда?

1 Ответ

1 голос
/ 15 декабря 2010

Первое, что вы должны сделать в ООП, это рассмотреть классы объектов.Какао использует архитектуру MVC (модель, представление, контроллер), поэтому классы должны соответствовать одной из этих трех категорий.Какао уже предоставляет класс NSTableView, который работает довольно хорошо, поэтому он оставляет модель и контроллер.

Существует несколько различных подходов к классу модели, которые вы можете использовать:

  • ВыМожно написать класс таблицы функций, который содержит значения x и y в отдельных массивах
  • Вы можете написать класс таблицы функций, который имеет один массив пар (x, y).
    • В этой или предыдущей реализации вы могли бы предоставить общедоступный интерфейс, который поддерживает оба устройства (т. Е. У них есть методы, которые возвращают ay при заданном x, и свойства, которые являются x, y и (x,у) коллекции).Некоторые детали реализации будут зависеть от того, как вы соединяете табличное представление с данными (связывания или более старый NSTableViewDataSource протокол ).
  • Вы также можете использоватьмассив значений x и создайте преобразователь значений .При таком подходе значения y существуют в табличном представлении, а не в модели.
  • И т. Д.

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

Для контроллера вы можете положиться на NSArrayController (который довольно хорошо работает с NSTableView) или создать свой собственный.Например, вы можете использовать NSMutableArray в качестве модели и создать контроллер, который отображает значения из массива в другие значения.Этот контроллер может выполнять отображение, используя блоки или некоторые классы функций, которые вы определяете.

Как видите, вариантов довольно много.Я собираюсь пойти с опцией, которая требует наименьшего кодирования: преобразователь значения, NSArrayController для контроллера и NSMutableArray (хранится в объекте, который также хранит преобразователь значения) для модели.Далее код должен храниться в файлах в соответствии со стандартным соглашением: каждый интерфейс и реализация находятся в отдельном файле с именем, равным классу, и расширением «.h» для интерфейсов и «.m» для реализации.Я также не буду беспокоиться об общих операторах импорта, таких как Cocoa / Cocoa.h и собственный интерфейс реализации каждого класса.

Во-первых, преобразователь значения.На самом деле их два, абстрактный суперкласс и конкретный подкласс.Это разделение позволяет легко добавлять другие типы функций позже.Суперкласс FunctionTransformer очень прост.Все, что нужно переопределить из своей базы, NSValueTransformer, это метод, который возвращает класс преобразованных значений, transformedValueClass:

@interface FunctionTransformer : NSValueTransformer 
+ (Class)transformedValueClass;
@end

@implementation Function
+ (Class)transformedValueClass {
    return [NSNumber class];
}
@end

Конкретный подкласс, LinearTransformer, необходимо переопределить основной метод преобразователей значения: transformedValue:.Поскольку линейные преобразования являются обратимыми, мы также предоставим reverseTransformedValue:.Также потребуются свойства для значений наклона и перехвата.

#import "FunctionTransformer.h"

@interface LinearTransformer : FunctionTransformer {
    NSNumber *m_;
    NSNumber *b_;
}
@property (nonatomic,retain) NSNumber *slope;
@property (nonatomic,retain) NSNumber *intercept;


+ (BOOL)allowsReverseTransformation;

-(id)init;
-(id)initWithSlope:(float)slope;
-(id)initWithIntercept:(float)intercept;
-(id)initWithSlope:(float)slope intercept:(float)intercept;

-(void)dealloc;

-(NSNumber*)transformedValue:(id)value;
-(NSNumber*)reverseTransformedValue:(id)value;
@end


@implementation LinearTransformer
@synthesize slope=m_, intercept=b_;

+(BOOL)allowsReverseTransformation {
    return YES;
}
-(id)initWithSlope:(float)m intercept:(float)b {
    if ((self = [super init])) {
        m_ = [[NSNumber alloc] initWithFloat:m];
        b_ = [[NSNumber alloc] initWithFloat:b];
    }
    return self;    
}

-(id)init {
    return [self initWithSlope:1.0 intercept:0.0];
}
-(id)initWithSlope:(float)slope {
    return [self initWithSlope:slope intercept:0.0];
}
-(id)initWithIntercept:(float)intercept {
    return [self initWithSlope:1.0 intercept:intercept];    
}


-(void)dealloc {
    [b release];
    [m release];
    [super dealloc];
}


-(NSNumber*)transformedValue:(id)value {
    return [NSNumber numberWithFloat:([value floatValue] * [m floatValue] + [b floatValue])];
}

-(NSNumber*)reverseTransformedValue:(id)value {
    return [NSNumber numberWithFloat:(([value floatValue] - [b floatValue]) / [m floatValue])];
}
@end

Для использования необходимо зарегистрировать определенный LinearTransformer, чтобы можно было установить наклон и перехват.Делегат приложения может владеть этим преобразователем (вместе с коллекцией значений x), или вы можете написать собственный контроллер.Мы собираемся написать класс модели, который объединяет значения x и преобразователь значений с именем FunctionTable.Настройка функции преобразователя требует подзадач: зарегистрировать преобразователь в качестве преобразователя значения (используя +setValueTransformer:forName:).Это означает, что нам нужно предоставить наш собственный установщик (setF:) для свойства преобразователя функции (f).

#import "FunctionTransformer.h"

extern NSString* const kFunctionTransformer;

@interface FunctionTable : NSObject {
    NSMutableArray *xs;
    FunctionTransformer *f;
}
@property (nonatomic,retain) IBOutlet NSMutableArray *xs;
@property (nonatomic,retain) IBOutlet FunctionTransformer *f;
@end

// FunctionTable.m:
#import "LinearTransformer.h"

NSString* const kFunctionTransformer = @"Function Transformer";

@implementation FunctionTable
@synthesize xs, f;

-(id) init {
    if ((self = [super init])) {
        xs = [[NSMutableArray alloc] init];
        self.f = [[LinearTransformer alloc] init];
        [f release];
    }
    return self;
}
-(void)dealloc {
    [f release];
    [xs release];
    [super dealloc];
}

-(void)setF:(FunctionTransformer *)func {
    if (func != f) {
        [f release];
        f = [func retain];
        [NSValueTransformer setValueTransformer:f forName:kFunctionTransformer];
    }
}
@end

По умолчанию FunctionTable использует LinearTransformer. Если вы хотите использовать другой, просто установите свойство FunctionTables f. Вы можете сделать это в Interface Builder (IB), используя привязки . Обратите внимание, что в этой упрощенной реализации преобразователь значения всегда регистрируется под именем «Function Transformer», фактически ограничивая вас одним FunctionTable. Более сложной схемой было бы дать каждому FunctionTable свое собственное имя преобразователя функции, которое будет использоваться при регистрации их собственного FunctionTransformer.

Чтобы все настроить:

  1. Откройте перо главного окна приложения в IB.
  2. Создание экземпляра NSArrayController и FunctionTable (и вашего пользовательского делегата приложения, если есть).
  3. К главному окну добавить:
    1. Кнопки для добавления и удаления элементов,
    2. меток и NSTextFields для наклона и точки пересечения,
    3. NSTableView.
  4. Установите заголовки таблиц на "x" и "y" (необязательно для работы приложения)
  5. Настройка соединений :
    • Попросите кнопки добавления и удаления отправить действия NSArrayController add: и remove:.
    • Привязать значения NSTextFields к путям клавиш f.slope и f.intercept FunctionTables.
    • Привязать значения обоих столбцов NSTableView к FunctionTables xs.
    • Установить преобразователь значения для второго столбца в «Функциональный преобразователь»
    • Привязать массив содержимого NSArrayController к клавише xs в FunctionTable.
    • Если у вас есть делегат приложения, подключите его к розетке владельца файла delegate.

Теперь соберите и запустите. Вы можете использовать кнопки добавления и удаления для добавления и удаления строк в / из таблицы. Вы можете редактировать столбцы «x» и «y» подряд (последний благодаря reverseTransformedValue:). Вы можете сортировать по столбцам «x» или «y». Вы можете изменить наклон и перехват, хотя вы не заметите обновления в таблице, если не выберете строки по отдельности.

Дополнительные темы

Чтобы исправить проблему обновления табличного представления, нам нужно распространить изменения свойств одного объекта (FunctionTransformer) на изменения свойств другого (a FunctionTable). У нас будет FunctionTable наблюдать изменения свойств его функционального преобразователя и, когда он FunctionTable получит уведомление об изменении любого такого свойства, отправит уведомление об изменении свойства xs ( что немного злоупотребляет, так как xs фактически не изменилось). Это станет немного волшебным, так что терпите меня.

Объект подписывается на изменения другого объекта, используя KVO метод addObserver:forKeyPath:options:context: другого объекта, и отменяет подписку, используя removeObserver:forKeyPath:. Эти методы просто нужно вызывать, а не писать. Уведомления обрабатываются методом observeValueForKeyPath:ofObject:change:context: объекта наблюдения, поэтому этот метод необходимо написать. Наконец, объект может отправлять свои собственные уведомления, вызывая willChangeValueForKey: и didChangeValueForKey:. Существуют и другие способы отправки уведомлений об изменении только части коллекции, но мы не будем их здесь использовать.

Наш FunctionTable может обрабатывать изменения подписки и отмены подписки, но затем он должен знать, какие свойства функционального преобразователя следует соблюдать, что означает, что вы не можете изменить тип преобразователя. Вы можете добавить методы к каждому конкретному преобразователю функции, чтобы подписаться и отписаться от наблюдателя:

@implementation LinearTransformer
...
-(void)addObserver:(NSObject *)observer 
                   options:(NSKeyValueObservingOptions)options 
                   context:(void *)context 
{
    [self addObserver:observer
           forKeyPath:@"slope"
              options:options
              context:context];
    [self addObserver:observer
           forKeyPath:@"intercept"
              options:options
              context:context];
}
-(void)removeObserver:(id)observer {
    [self removeObserver:observer forKeyPath:@"slope"];
    [self removeObserver:observer forKeyPath:@"intercept"];
}
@end

Однако это потребует значительного повторения кода в каждом методе и для каждого конкретного преобразователя функции.Использование некоторой магии ( отражение и замыкания или, как их называют в Objective-C, блоков ( [2] ))мы можем добавить методы (названные addObserver:options:context: и removeObserver:, так как они функционально аналогичны методам KVO для подписки и отписки) к FunctionTransformer или даже к NSObject.Поскольку наблюдение всех свойств объекта не ограничивается FunctionTransformer s, мы добавим методы к NSObject.Чтобы это работало, вам понадобятся либо OS X 10.6, либо PLBlocks и OS X 10.5.

Давайте начнем сверху вниз с изменениями FunctionTable.Теперь появились новые подзадачи при настройке функции преобразователя: отмена подписки на изменения старого преобразователя и подписка на изменения нового.Таким образом, метод setF: необходимо обновить, чтобы использовать новые методы NSObject, которые будут определены в заголовке с именем "NSObject_Properties.h".Обратите внимание, что нам пока не нужно беспокоиться о реализации этих методов.Мы можем использовать их здесь, веря, что позже напишем подходящие реализации.FunctionTable также нужен новый метод для обработки уведомлений об изменениях (observeValueForKeyPath:ofObject:change:context:, о котором говорилось ранее).

#import "NSObject_Properties.h"
@interface FunctionTable
...
-(void)setF:(FunctionTransformer *)func {
    if (func != f) {
        [f removeObserver:self];
        [f release];
        f = [func retain];
        [f addObserver:self
               options:NSKeyValueObservingOptionPrior
               context:NULL];
        [NSValueTransformer setValueTransformer:f forName:kFunctionTransformer];
    }
}


- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (object == f) {
        if ([[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
            [self willChangeValueForKey:@"xs"];
        } else {
            [self didChangeValueForKey:@"xs"];
        }
    }
}

Далее мы пишем новые методы для NSObject.Методы подписки или отмены подписки на изменения будут зацикливаться на свойствах объекта, поэтому нам понадобится вспомогательный метод forEachProperty для выполнения цикла.Этот вспомогательный метод примет блок, который он вызывает для каждого свойства.Методы подписки и отмены подписки будут просто вызывать forEachProperty, передавая блок, который вызывает стандартные методы KVO (addObserver:forKeyPath:options:context: и removeObserver:forKeyPath:) для каждого свойства для добавления или удаления подписок.

//NSObject_Properties.h
#import <Cocoa/Cocoa.h>
#import <objc/runtime.h>

@interface NSObject (Properties)
typedef void (^PropertyBlock)(objc_property_t prop, NSString *name);
-(void)forEachProperty:(PropertyBlock)block;

-(void)addObserver:(id)observer options:(NSKeyValueObservingOptions)options context:(void *)context;
-(void)removeObserver:(id)observer;
@end

// NSObject_Properties.m:
...
@implementation NSObject (Properties)
-(void)forEachProperty:(PropertyBlock)block {
    unsigned int propCount, i;
    objc_property_t * props = class_copyPropertyList([self class], &propCount);
    NSString *name;

    for (i=0; i < propCount; ++i) {
        name = [[NSString alloc] 
                    initWithCString:property_getName(props[i]) 
                    encoding:NSUTF8StringEncoding];
        block(props[i], name);
        [name release];
    }

    free(props);
}

-(void)addObserver:(NSObject *)observer 
           options:(NSKeyValueObservingOptions)options 
           context:(void *)context 
{
    [self forEachProperty:^(objc_property_t prop, NSString *name) {
        [self addObserver:observer
               forKeyPath:name
                  options:options
                  context:context];

    }];
}
-(void)removeObserver:(id)observer {
    [self forEachProperty:^(objc_property_t prop, NSString *name) {
        [self removeObserver:observer forKeyPath:name];
    }]; 
}
@end
...