这是一个老问题,但作为参考,我遇到了一个类似的问题,在编辑会话开始时调用了controlTextDidEndEditing:。
我的解决方法是检查文本字段是否仍然具有焦点(即光标):
func controlTextDidEndEditing(_ obj: Notification) {
guard
let textField = obj.object as? NSTextField,
!textField.isFocused
else {
return
}
...
}
public extension NSTextField
{
public var isFocused:Bool {
if
window?.firstResponder is NSTextView,
let fieldEditor = window?.fieldEditor(false, for: nil),
let delegate = fieldEditor.delegate as? NSTextField,
self == delegate
{
return true
}
return false
}
}
自我说明:
我在向NSOutlineView 添加新项目并使用NSOutlineView.editColumn(row:,with:,select) 使其可编辑时遇到了这个问题。
controlTextDidEndEditing() 将在编辑会话开始时立即调用。
事实证明这是第一响应者/动画竞争条件。我在插入行时使用了NSTableView.AnimationOptions.slideDown 动画,然后使行可编辑。
这里的问题是该行在仍处于动画状态时是可编辑的。动画结束后,第一响应者切换到窗口并返回到文本字段,这会导致调用 controlTextDidEndEditing()。
outlineView.beginUpdates()
outlineView.insertItems(at: IndexSet(integer:atIndex),
inParent: intoParent == rootItem ? nil : intoParent,
withAnimation: .slideDown) // Animating!
outlineView.endUpdates()
// Problem: the animation above won't have finished leading to first responder issues.
self.outlineView.editColumn(0, row: insertedRowIndex, with: nil, select: true)
解决方案 1:
插入行时不要使用动画。
解决方案 2:
将beginUpdates/endUpdates 包装到NSAnimationContext 组中,添加一个完成处理程序以仅在动画完成后开始编辑。
调试提示:
- 观察窗口控制器中
firstResponder 的变化
- 在
controlTextDidEndEditing() 中放置一个断点,并仔细查看堆栈跟踪,看看是什么导致它被调用。在我的案例中,它泄露的是对动画调用的引用。
- 要重现,请将
beginUpdates/endUpdates 包裹在 NSAnimationContext 中,并将动画持续时间增加到几秒钟。