我需要编写NSTextField
代码,它基本上与Spotlight搜索中的计算器功能相似。我已成功使用NSExpression
解析实际的输入表达式并计算结果,但它是我坚持使用的视图部分。
如何在用户输入时在文本字段中呈现答案,但不将其作为输入的一部分?我是否可能必须继承NSTextField?
只是创建一个NSFormatter
并没有给我提供我需要的所有功能。我可以使用委托的controlTextDidEndEditing:
消息立即计算并用结果替换文本字段,但右边的动态总和往返于格式化程序。
有没有人知道一个接近我想做的事情的例子?
我对Cocoa很陌生,并且在知道要实现这一目标的哪些组件方面遇到了一些麻烦。
答案 0 :(得分:2)
首先,使用NSTextView
代替NSTextField
会更简单(我认为),因为使用NSTextField
你会进入“字段编辑器”疯狂,其中文本字段在具有焦点时使用封面下的文本视图(字段编辑器)。我们需要使用文本视图委托方法来实现它,所以我们只使用NSTextView
。
基本思想是我们将使用属性字符串以大黑色字符显示公式,然后使用较小的灰色字符显示答案。每当用户更改输入时,我们将删除旧答案并附加新答案。
对于玩具实施,我们只会在app委托中执行所有操作。我们有一个连接到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
对此不是一个好的数学解析器,原因有两个:
%
来表示格式说明符,这在用户输入的字符串中并不是一个好主意。您应该考虑使用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;
}
答案 1 :(得分:0)
您可以使用key-value validation执行此操作。这使您有机会更改用户输入而不是简单地拒绝它。你应该打开&#34;立即验证&#34;或者对于文本字段。
您可以使用属性识别添加的结果文本,并在每次更改时替换它们。
只是一个想法。