要点:我有一堆嵌套在一个MainView
中的观点。当我在其中一个视图中嵌套的becomeFirstResponder()
上致电NSTextField
时,MainView
内的每个视图突然跳转到半任意的旧位置。
实际原因可能是许多事情中的任何一个,其中可能是我只写了一个星期的Cocoa。
这是实际结构:
MainView: NSView
├ ContactTile: NSView (layer-backed)
| └ /* several CALayers */
├ ContactTile
├ …
├ ContactTile
└ StatusTile: NSView (layer-backed)
├ SearchIcon: NSView (layer-backed)
| └ SearchIconLayer: CALayer
└ NSTextField
我使用ReactiveCocoa支持所有核心业务逻辑,异步调度回主UI线程以进行与绘图相关的操作。
StatusTile
初始化并在创建MainView
时立即添加为子视图,其位置设置为屏幕的右上角区域。 ContactTiles
是动态创建和添加的 - 创建和子视图添加发生在一个ReactiveCocoa观察者中(使初始位置保持为(0, 0)
),并在另一个"重新布局"观察者所有ContactTile
以及StatusTile
都是动画的,并且定位的代码看起来大致如下:
let anim = CABasicAnimation.init(keyPath: "position");
let (from, to) = /* bunch of positional calculation */;
anim.fromValue = NSValue.init(point: from);
anim.toValue = NSValue.init(point: to);
tile.layer!.removeAnimation("contacttile-layout");
tile.layer!.addAnimation(anim, forKey: "contacttile-layout");
tile.layer!.position = to;
只要我不与becomeFirstResponder
做任何事情,这一切都能很好地发挥作用;瓷砖从预期位置生成动画,并按预期保持在那里。如果我在他们的.position
办理登机手续,他们就会完全符合我的期望。
但现在我添加代码,在单独的ReactiveCocoa观察器中侦听全局热键。该全局热键信号同时触发重新布局,导致您看到的动画/布局代码。但它也会触发一个单独的观察者,在上面的textField.becomeFirstResponder()
视图中调用StatusTile
。
GlobalInteraction.sharedInstance.activated.observeNext { activated in
if activated {
dispatch_async(dispatch_get_main_queue(), { self._searchField.becomeFirstResponder(); });
} else {
self._searchField.stringValue = "";
}
}
现在发生的事情是动画完全按照预期进行,但是在动画完成时,每个元素重置到其初始位置,如前几段所述:StatusTile
恢复到其初始右上角位置,并且每个ContactTile
都会恢复为(0, 0)
。
如果我检查他们实际的.position
静止状态,他们确实会报告(0, 0)
。但如果我在上面的计算块之后立即检查他们的.position
,那么一切都是我期望的。甚至更奇怪的是,.becomeFirstResponder()
调用非常一致地发生之前我执行上面的重新布局动画设置。因此,就我的代码而言,无论如何,在之后,我的位置的调用都会被说明并完成。
当随后发生更改布局的内容时,例如在文本字段或其他外部输入中键入内容,即使文本字段具有焦点,所有内容也会恢复正常工作。只有在最初激活时,一切都会反弹。
一些可能相关的怪癖: