可可 – 在NSPopover中使用NSNumberFormatter

可可 – 在NSPopover中使用NSNumberFormatter,第1张

概述有没有办法得到一个NSNumberFormatter(或大概任何其他NSFormatter)在NSPopover工作? NSTextField在popover中的值绑定到NSViewController的representObject.当一个无效的数字输入到该字段(例如“asdf”)时,表示该值无效的表格将显示在包含提供该d出窗口的NSView的NSWindow中. 一旦您单击确定,您会得到以下回 有没有办法得到一个NSNumberFormatter(或大概任何其他NSFormatter)在NSPopover工作?

NSTextFIEld在popover中的值绑定到NSVIEwController的representObject.当一个无效的数字输入到该字段(例如“asdf”)时,表示该值无效的表格将显示在包含提供该d出窗口的NSVIEw的NSWindow中.

一旦您单击确定,您会得到以下回溯:

* thread #1: tID = 0x4e666a,0x00007fff931f9097 libobjc.A.dylib`objc_msgSend + 23,queue = 'com.apple.main-thread',stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)frame #0: 0x00007fff931f9097 libobjc.A.dylib`objc_msgSend + 23frame #1: 0x00007fff8a1fa6c8 AppKit`-[NSTextVIEw(NSSharing) becomeKeyWindow] + 106frame #2: 0x00007fff8a080941 AppKit`-[NSWindow(NSWindow_theme) acquireKeyAppearance] + 207frame #3: 0x00007fff8a0800df AppKit`-[NSWindow becomeKeyWindow] + 1420frame #4: 0x00007fff8a07f5c6 AppKit`-[NSWindow _changeKeyAndMainlimitedOK:] + 803frame #5: 0x00007fff8a1a205d AppKit`-[NSWindow _orderOutAndCalcKeyWithCounter:stillVisible:docWindow:] + 1156frame #6: 0x00007fff8a0876c5 AppKit`-[NSWindow _reallyDoOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 3123frame #7: 0x00007fff8a0867f0 AppKit`-[NSWindow _doOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 786frame #8: 0x00007fff8a086470 AppKit`-[NSWindow orderWindow:relativeTo:] + 162frame #9: 0x00007fff8a1a1425 AppKit`__18-[NSWindow _close]_block_invoke + 443frame #10: 0x00007fff8a1a1230 AppKit`-[NSWindow _close] + 370frame #11: 0x00007fff8a2d0565 AppKit`__106-[NSApplication(NSErrorPresentation) presentError:modalForWindow:delegate:dIDPresentSelector:contextInfo:]_block_invoke3221 + 50frame #12: 0x00007fff8a2d02f7 AppKit`-[NSApplication(NSErrorPresentation) _something:waspresenteDWithResult:soContinue:] + 18frame #13: 0x00007fff8a28fe9d AppKit`-[NSAlert dIDEnDalert:returnCode:contextInfo:] + 90frame #14: 0x00007fff8a28f8c2 AppKit`-[NSWindow endSheet:returnCode:] + 368frame #15: 0x00007fff8a28f49d AppKit`-[NSAlert buttonpressed:] + 107frame #16: 0x00007fff8a1543d0 AppKit`-[NSApplication sendAction:to:from:] + 327frame #17: 0x00007fff8a15424e AppKit`-[NSControl sendAction:to:] + 86frame #18: 0x00007fff8a1a0d7d AppKit`-[NSCell _sendActionFrom:] + 128frame #19: 0x00007fff8a1ba715 AppKit`-[NSCell trackMouse:inRect:ofVIEw:untilMouseUp:] + 2316frame #20: 0x00007fff8a1b9ae7 AppKit`-[NSbuttonCell trackMouse:inRect:ofVIEw:untilMouseUp:] + 487frame #21: 0x00007fff8a1b91fd AppKit`-[NSControl mouseDown:] + 706frame #22: 0x00007fff8a13ad08 AppKit`-[NSWindow sendEvent:] + 11296frame #23: 0x00007fff8a0d9744 AppKit`-[NSApplication sendEvent:] + 2021frame #24: 0x00007fff89f29a29 AppKit`-[NSApplication run] + 646frame #25: 0x00007fff89f14803 AppKit`NSApplicationMain + 940

在objc_msgSend中崩溃时的寄存器是:

我猜这是因为临时popover的窗口在表单显示后消失了,当前编辑器和任何可以响应选择器的对象也是如此.

将popover行为设置为NSPopoverBehaviorSemitransIEnt有所帮助,但如果在文本字段中d出窗口被无效的值解除,则仍会抛出该异常.

在这一点上,我可以想到的是避免这个问题是手动验证数值.呸.

更新1

正如Brian Webster在下面发现的,这是AppKit的一个根本问题.

由于我的验证需求非常简单(只是正整数),解决方法是在KVC对象中进行手动验证,该对象用作NSPopover显示的NSVIEwController中的givenObject.由于NSTextFIEld真的要使用字符串值,所以使用-valueForKey:和-setValue:forKey:用于转换标量值.当您打开文本字段中的绑定值“立即验证”时,只要文本字段发生更改,就会调用验证方法.

(在你询问之前,NSValuetransformer不能做这个工作,因为它没有参与验证过程,只有在填充字段或保存更改时才会被调用,一旦用户输入了一些无效数据 – 作为NSFormatter将会做.)

这是我所做的一切:

现在我需要洗澡

更新2

感谢@PixelCutCompany提供的几个有用的提示,了解如何在PaintCode应用程序中执行此 *** 作:

https://twitter.com/PixelCutCompany/status/441695942774104064
https://twitter.com/PixelCutCompany/status/441696198140125184

我想出了这个:

基本上,您可以通过始终提供有效的值来避免工作表或对话框的问题.上面的代码在分配默认值时考虑了最小值和最大值.子类也考虑了零或空字符串以及钳位值.

这让我感觉不那么脏.

(lldb) reg readGeneral Purpose Registers: rax = 0x0000610000190740 rbx = 0x0000610000190740 rcx = 0x0000000000000080 rdx = 0x00007fff8a97fd93 "currentEditor" rdi = 0x0000610000190740 rsi = 0x00007fff8a9612bf "respondsToSelector:" rbp = 0x00007fff5fbfeae0 rsp = 0x00007fff5fbfeab8 r8 = 0x000000000000002e r9 = 0xffff9fffffeb1bbf r10 = 0x00007fff8a9612bf "respondsToSelector:" r11 = 0xbaddbe5c3e96bead r12 = 0x0000610000053830 r13 = 0x00007fff931f9080 libobjc.A.dylib`objc_msgSend r14 = 0x000
* thread #1: tID = 0x4e666a,stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)frame #0: 0x00007fff931f9097 libobjc.A.dylib`objc_msgSend + 23frame #1: 0x00007fff8a1fa6c8 AppKit`-[NSTextVIEw(NSSharing) becomeKeyWindow] + 106frame #2: 0x00007fff8a080941 AppKit`-[NSWindow(NSWindow_theme) acquireKeyAppearance] + 207frame #3: 0x00007fff8a0800df AppKit`-[NSWindow becomeKeyWindow] + 1420frame #4: 0x00007fff8a07f5c6 AppKit`-[NSWindow _changeKeyAndMainlimitedOK:] + 803frame #5: 0x00007fff8a1a205d AppKit`-[NSWindow _orderOutAndCalcKeyWithCounter:stillVisible:docWindow:] + 1156frame #6: 0x00007fff8a0876c5 AppKit`-[NSWindow _reallyDoOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 3123frame #7: 0x00007fff8a0867f0 AppKit`-[NSWindow _doOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 786frame #8: 0x00007fff8a086470 AppKit`-[NSWindow orderWindow:relativeTo:] + 162frame #9: 0x00007fff8a1a1425 AppKit`__18-[NSWindow _close]_block_invoke + 443frame #10: 0x00007fff8a1a1230 AppKit`-[NSWindow _close] + 370frame #11: 0x00007fff8a2d0565 AppKit`__106-[NSApplication(NSErrorPresentation) presentError:modalForWindow:delegate:dIDPresentSelector:contextInfo:]_block_invoke3221 + 50frame #12: 0x00007fff8a2d02f7 AppKit`-[NSApplication(NSErrorPresentation) _something:waspresenteDWithResult:soContinue:] + 18frame #13: 0x00007fff8a28fe9d AppKit`-[NSAlert dIDEnDalert:returnCode:contextInfo:] + 90frame #14: 0x00007fff8a28f8c2 AppKit`-[NSWindow endSheet:returnCode:] + 368frame #15: 0x00007fff8a28f49d AppKit`-[NSAlert buttonpressed:] + 107frame #16: 0x00007fff8a1543d0 AppKit`-[NSApplication sendAction:to:from:] + 327frame #17: 0x00007fff8a15424e AppKit`-[NSControl sendAction:to:] + 86frame #18: 0x00007fff8a1a0d7d AppKit`-[NSCell _sendActionFrom:] + 128frame #19: 0x00007fff8a1ba715 AppKit`-[NSCell trackMouse:inRect:ofVIEw:untilMouseUp:] + 2316frame #20: 0x00007fff8a1b9ae7 AppKit`-[NSbuttonCell trackMouse:inRect:ofVIEw:untilMouseUp:] + 487frame #21: 0x00007fff8a1b91fd AppKit`-[NSControl mouseDown:] + 706frame #22: 0x00007fff8a13ad08 AppKit`-[NSWindow sendEvent:] + 11296frame #23: 0x00007fff8a0d9744 AppKit`-[NSApplication sendEvent:] + 2021frame #24: 0x00007fff89f29a29 AppKit`-[NSApplication run] + 646frame #25: 0x00007fff89f14803 AppKit`NSApplicationMain + 940
* thread #1: tID = 0x4e666a,stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)frame #0: 0x00007fff931f9097 libobjc.A.dylib`objc_msgSend + 23frame #1: 0x00007fff8a1fa6c8 AppKit`-[NSTextVIEw(NSSharing) becomeKeyWindow] + 106frame #2: 0x00007fff8a080941 AppKit`-[NSWindow(NSWindow_theme) acquireKeyAppearance] + 207frame #3: 0x00007fff8a0800df AppKit`-[NSWindow becomeKeyWindow] + 1420frame #4: 0x00007fff8a07f5c6 AppKit`-[NSWindow _changeKeyAndMainlimitedOK:] + 803frame #5: 0x00007fff8a1a205d AppKit`-[NSWindow _orderOutAndCalcKeyWithCounter:stillVisible:docWindow:] + 1156frame #6: 0x00007fff8a0876c5 AppKit`-[NSWindow _reallyDoOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 3123frame #7: 0x00007fff8a0867f0 AppKit`-[NSWindow _doOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 786frame #8: 0x00007fff8a086470 AppKit`-[NSWindow orderWindow:relativeTo:] + 162frame #9: 0x00007fff8a1a1425 AppKit`__18-[NSWindow _close]_block_invoke + 443frame #10: 0x00007fff8a1a1230 AppKit`-[NSWindow _close] + 370frame #11: 0x00007fff8a2d0565 AppKit`__106-[NSApplication(NSErrorPresentation) presentError:modalForWindow:delegate:dIDPresentSelector:contextInfo:]_block_invoke3221 + 50frame #12: 0x00007fff8a2d02f7 AppKit`-[NSApplication(NSErrorPresentation) _something:waspresenteDWithResult:soContinue:] + 18frame #13: 0x00007fff8a28fe9d AppKit`-[NSAlert dIDEnDalert:returnCode:contextInfo:] + 90frame #14: 0x00007fff8a28f8c2 AppKit`-[NSWindow endSheet:returnCode:] + 368frame #15: 0x00007fff8a28f49d AppKit`-[NSAlert buttonpressed:] + 107frame #16: 0x00007fff8a1543d0 AppKit`-[NSApplication sendAction:to:from:] + 327frame #17: 0x00007fff8a15424e AppKit`-[NSControl sendAction:to:] + 86frame #18: 0x00007fff8a1a0d7d AppKit`-[NSCell _sendActionFrom:] + 128frame #19: 0x00007fff8a1ba715 AppKit`-[NSCell trackMouse:inRect:ofVIEw:untilMouseUp:] + 2316frame #20: 0x00007fff8a1b9ae7 AppKit`-[NSbuttonCell trackMouse:inRect:ofVIEw:untilMouseUp:] + 487frame #21: 0x00007fff8a1b91fd AppKit`-[NSControl mouseDown:] + 706frame #22: 0x00007fff8a13ad08 AppKit`-[NSWindow sendEvent:] + 11296frame #23: 0x00007fff8a0d9744 AppKit`-[NSApplication sendEvent:] + 2021frame #24: 0x00007fff89f29a29 AppKit`-[NSApplication run] + 646frame #25: 0x00007fff89f14803 AppKit`NSApplicationMain + 9400012a500
r15 = 0x00007fff931f9080 libobjc.A.dylib`objc_msgSend
rip = 0x00007fff931f9097 libobjc.A.dylib`objc_msgSend + 23
rflags = 0x0000000000010246
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x00000000c0100000
- (ID)valueForKey:(Nsstring *)key{ if ([key isEqualToString:@"property1"]) { return [Nsstring stringWithFormat:@"%zd",_property1]; } else if ([key isEqualToString:@"property2"]) { return [Nsstring stringWithFormat:@"%zd",_property2]; } else { return [super valueForKey:key]; }}- (BOol)valIDateValue:(inout ID *)iovalue forKey:(Nsstring *)inKey error:(out NSError **)outError{ if (! *iovalue) { *iovalue = @"0"; } else if ([*iovalue isKindOfClass:[Nsstring class]]) { Nsstring *inputString = [[(Nsstring *)*iovalue copy] autorelease]; inputString = [inputString stringByReplacingOccurrencesOfString:@"," withString:@""]; NSInteger integerValue = [inputString integerValue]; if (integerValue < 0) { integerValue = -integerValue; } *iovalue = [Nsstring stringWithFormat:@"%zd",integerValue]; } return YES;}- (voID)setValue:(ID)value forKey:(Nsstring *)key{ if ([value isKindOfClass:[Nsstring class]]) { if ([key isEqualToString:@"property1"]) { _property1 = [value integerValue]; } else if ([key isEqualToString:@"property2"]) { _property2 = [value integerValue]; } else { [super setValue:value forKey:key]; } } else { [super setValue:value forKey:key]; }}@interface PopupNumberFormatter : NSNumberFormatter@end@implementation PopupNumberFormatter- (BOol)getobjectValue:(out ID *)anObject forString:(Nsstring *)aString range:(inout NSRange *)rangep error:(out NSError **)error{ NSNumber *minimum = [self minimum]; NSNumber *maximum = [self maximum]; if (aString == nil || [aString length] == 0) { if (minimum) { *anObject = minimum; } else if (maximum) { *anObject = maximum; } else { *anObject = [NSNumber numberWithInteger:0]; } } else { if (! [super getobjectValue:anObject forString:aString range:rangep error:nil]) { // if the superclass can't parse the string,assign a reasonable default if (minimum) { *anObject = minimum; } else if (maximum) { *anObject = maximum; } else { *anObject = [NSNumber numberWithInteger:0]; } } else { // clamp the parsed value to a minimum and maximum (if set) if (minimum && [*anObject compare:minimum] == NSOrderedAscending) { *anObject = minimum; } else if (maximum && [*anObject compare:maximum] == NSOrderedDescending) { *anObject = maximum; } } } return YES;}@end
解决方法 我设立了一个测试项目,看看是否可以重现这个问题,而且我也有同样的行为.以下是事件的顺序:

>当您在文本字段中输入时,会触发绑定,该绑定将尝试通过NSNumberFormatter验证字段中的值.
>当它失败时,绑定系统通过响应者链显示一个NSError对象.这引起了NSApplication,它将错误作为窗口上的一张表格呈现.
>表单的外观会触发d出窗口关闭,这反过来又会触发相同的绑定,从而尝试显示另一个错误.但是,由于窗口上已经显示了一个工作表,所以第二个错误不会显示.如果您更改绑定选项并启用“始终显示应用程序模态警报”(将在单独的窗口而不是工作表中显示错误),则会显示两个单独的警报窗口.

我认为这是一个错误 – 在错误中抛出了一个循环的AppKit,而当它试图混乱字段编辑器(这是堆栈跟踪中的NSTextVIEw)的某个地方的时候,它最终会在现在被释放的NSTextFIEld.

我发现最好的解决方法是实现-willPresentError:在NSVIEwController子类中,我用来控制popover,像这样:

- (NSError *)willPresentError:(NSError *)error{    NSMutableDictionary* userInfo = [[error userInfo] mutablecopy];    [self.numberTextFIEld unbind:@"value"];    [userInfo setValue:nil forKey:NSRecoveryAttempterErrorKey];    [userInfo setValue:nil forKey:NSLocalizedRecoveryOptionsErrorKey];    return [NSError errorWithDomain:[error domain] code:[error code] userInfo:userInfo];}

取消绑定:调用删除绑定,以便在popover关闭时不会尝试重新验证文本字段.由于当显示错误时,popover将会消失,因此假设您每次显示时都会从头开始创建popover,而不会重复使用,这不会有任何不良影响.

另外,由于当它们所指的字段已经消失时,“OK”和“discard Change”按钮不再是很有意义的,所以在将它传递给AppKit进行显示之前,我将该绑定系统的恢复权限从该错误中删除.这样,它只是使用“确定”按钮来说“值X无效”,除了关闭错误窗口之外什么也不做.

请注意,只有在绑定上启用了“始终显示应用程序模态警报”时,此 *** 作才起作用.否则,如果将要将显示错误作为工作表,至少不在视图控制器上,则AppPit似乎不会调用willPresentError:方法.您可能可以在响应者链中的其他位置插入逻辑,例如.主窗口的控制器,如果要保留表单的行为.

我会把它留给你来决定这是否比手动验证这些值更加丑陋. 总结

以上是内存溢出为你收集整理的可可 – 在NSPopover中使用NSNumberFormatter全部内容,希望文章能够帮你解决可可 – 在NSPopover中使用NSNumberFormatter所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1026639.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存