Во-первых, это будет намного проще (я думаю), чтобы использовать NSTextView
вместо NSTextField
, потому что с NSTextField
вы получаете в «поле редактор» безумие, где текстовое поле используется текстовое представление (редактор поля) под обложками, когда он имеет фокус. Нам нужно использовать методы делегирования текстового представления для его реализации, поэтому давайте просто использовать NSTextView
.
Основная идея состоит в том, что мы будем использовать атрибутированную строку для отображения формулы в больших черных символах, за которой следует ответ в более маленьких серых символах. Каждый раз, когда пользователь меняет ввод, мы удаляем старый ответ и добавляем новый ответ.
Для реализации игрушек мы просто сделаем все, что угодно, в делегате приложения. У нас есть выход, соединенный с точки зрения текста в XIB:
@interface AppDelegate() <NSTextDelegate, NSTextViewDelegate>
@property (weak) IBOutlet NSWindow *window;
@property (strong) IBOutlet NSTextView *textView;
@end
В XIB, delegate
выходе текстового вида не подключен к приложению делегата.
Мы также должны хранить длину ответа (так что мы можем удалить его перед добавлением нового ответа) и атрибуты текста для формулы и ответ:
@implementation AppDelegate {
NSUInteger answerLength;
NSDictionary *formulaAttributes;
NSDictionary *answerAttributes;
}
- (void)awakeFromNib {
[super awakeFromNib];
formulaAttributes = @{
NSFontAttributeName: [NSFont systemFontOfSize:32 weight:NSFontWeightLight]
};
answerAttributes = @{
NSFontAttributeName: [NSFont systemFontOfSize:24 weight:NSFontWeightLight],
NSForegroundColorAttributeName: [NSColor grayColor]
};
[self updateAnswerInTextView];
}
Всякий раз, когда содержимое изменение текстового вида, мы хотим удалить старый ответ и добавить новый ответ. Нам также нужно сохранить выбранный диапазон перед изменением текста и восстановить его после, поскольку текстовое представление помещает точку вставки в конец текста, когда мы его модифицируем. Последнее: пользователь мог вставить атрибутный текст в текстовое представление, и мы хотим удалить любые атрибуты на вложенном тексте. Поэтому мы удаляем атрибуты текста после удаления старого ответа.Мы делаем все это в NSTextDelegate
методе:
- (void)textDidChange:(NSNotification *)notification {
[self updateAnswerInTextView];
}
- (void)updateAnswerInTextView {
NSRange selectedRange = self.textView.selectedRange;
[self removeAnswerFromTextView];
[self applyFormulaAttributesToTextView];
[self appendAnswerToTextView];
self.textView.selectedRange = selectedRange;
}
Вот вспомогательные методы:
- (void)removeAnswerFromTextView {
NSTextStorage *storage = self.textView.textStorage;
[storage replaceCharactersInRange:self.answerRange withString:@""];
answerLength = 0;
}
- (void)applyFormulaAttributesToTextView {
NSTextStorage *storage = self.textView.textStorage;
[storage setAttributes:formulaAttributes range:NSMakeRange(0, storage.length)];
}
- (void)appendAnswerToTextView {
id answer = [self answer];
NSString *answerString = [NSString stringWithFormat:@" = %@", answer];
NSAttributedString *richAnswer = [[NSAttributedString alloc] initWithString:answerString attributes:answerAttributes];
[self.textView.textStorage appendAttributedString:richAnswer];
answerLength = answerString.length;
}
- (NSRange)answerRange {
NSTextStorage *storage = self.textView.textStorage;
return NSMakeRange(storage.length - answerLength, answerLength);
}
- (id)answer {
@try {
NSExpression *expression = [NSExpression expressionWithFormat:self.textView.textStorage.string];
return [expression expressionValueWithObject:nil context:nil];
}
@catch (NSException *exception) {
return @"???";
}
}
Обратите внимание, что NSExpression
не очень хорошая математику анализатора для этого, по двум причинам:
- Он выдает исключение, если есть ошибка синтаксического анализа, а Objective-C на самом деле не очищает вещи после исключения, поэтому могут быть утечки памяти или что-то еще хуже.
- Он использует
%
, чтобы указать спецификатор формата, что на самом деле не является отличной идеей в строке, введенной пользователем.
Вы должны изучить вместо этого the old Objective-C version of DDMathParser или другую библиотеку.
В любом случае, код выше оценивает формулу пользователя и отображает ответ, но у него есть проблема: пользователь может выбрать и изменить часть ответа строки. Чтобы исправить это, нам нужно реализовать NSTextViewDelegate
способ ограничить выбранный диапазон только формула часть текста:
- (NSRange)textView:(NSTextView *)textView willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange toCharacterRange:(NSRange)newSelectedCharRange {
NSUInteger answerStart = textView.textStorage.length - answerLength;
NSUInteger newSelectedEnd = NSMaxRange(newSelectedCharRange);
if (newSelectedEnd > answerStart) {
newSelectedEnd = answerStart;
NSUInteger newSelectedStart = MIN(newSelectedCharRange.location, newSelectedEnd);
newSelectedCharRange.location = newSelectedStart;
newSelectedCharRange.length = newSelectedEnd - newSelectedStart;
}
return newSelectedCharRange;
}
Если вы хотите динамически оценивать то, что набираясь как это набираясь (в противоположность после) , загляните в 'controlTextDidChange:': http://stackoverflow.com/a/11488611/277952 – NSGod
Я обновил вопросы, чтобы быть более явным. В основном моя проблема заключается в отображении ответа в поле (см. Рис.), Поскольку пользователь вводит без кругового отключения, что возвращается в форматтер. Я уже могу рассчитать выражение как пользовательский тип, я просто хочу отобразить последний ответ. –
Отредактировано ваше название, чтобы описать конкретную трудность, с которой вы столкнулись, а не [общий запрос для компонента] (http://meta.stackexchange.com/a/124930/159251). Это должно расширить область потенциальных решений. –