以下问题发生在 Xcode 16.2、iOS18 SDK 构建目标为 iOS17 的情况下
我有一个扩展UIBarButtonItem
来支持闭包而不是目标/动作:
extension UIBarButtonItem {
typealias Closure = () -> Void
private class UIBarButtonItemClosureWrapper: NSObject {
let closure: Closure
init(_ closure: @escaping Closure) {
self.closure = closure
}
deinit {
print("DEINIT")
}
}
private enum AssociatedKeys {
static var targetClosure = 1
}
convenience init(title: String?, style: UIBarButtonItem.Style, closure: @escaping Closure) {
self.init(title: title, style: style, target: nil, action: #selector(closureAction))
objc_setAssociatedObject(self, &AssociatedKeys.targetClosure, UIBarButtonItemClosureWrapper(closure), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
convenience init(image: UIImage?, style: UIBarButtonItem.Style, closure: @escaping Closure) {
self.init(image: image, style: style, target: nil, action: #selector(closureAction))
objc_setAssociatedObject(self, &AssociatedKeys.targetClosure, UIBarButtonItemClosureWrapper(closure), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
@objc
func closureAction() {
guard let closureWrapper = objc_getAssociatedObject(self, &AssociatedKeys.targetClosure) as? UIBarButtonItemClosureWrapper else { return }
closureWrapper.closure()
}
}
然而,一些非常奇怪的事情发生了,我不知道为什么。
UIViewController
我在中的a 中使用它navigationItem.rightBarButtonItems
,并且该 viewcontroller 是 的根 viewcontroller UINavigationController
。一切正常,直到我将 添加到包含在该 viewcontroller 的子 viewcontroller 中的TextField
SwiftUI 中。View
UIHostingController
总结一下:
UINavigationController -> UIViewController -> UIHostingController -> TextField
一旦我将焦点放在 上TextField
,按钮就不再起作用。您可以看到它在点击时变得半透明(因此它正在记录点击),但不再调用闭包。如果我将一个使用标准目标/动作系统的按钮放在它旁边,该按钮将继续工作。
这是UIViewController
代码:
override func viewDidLoad() {
super.viewDidLoad()
let item = UIBarButtonItem(title: "TargetAction", style: .done, target: self, action: #selector(buttonTapped))
let item2 = UIBarButtonItem(title: "ClosureAction", style: .done) {
print("CLOSURE TEST")
}
self.navigationItem.rightBarButtonItems = [item, item2]
}
@objc func buttonTapped() {
print("TARGET TEST")
}
这是View
代码:
struct MyView: View {
var body: some View {
Form {
TextField("name", text: .constant("text"))
}
}
Target Test
每次我点击按钮时都会打印。每次Closure Test
我点击另一个按钮时都会打印,直到我通过点击 TextField 来聚焦它。从那时起Target Test
仍然有效,但Closure Test
不再打印。
TextField 的焦点和键盘外观是否会在我不知道的后台对导航栏进行某些操作,从而以某种方式破坏相关对象?Deinit
永远不会打印,并且放置断点ClosureAction
显示该函数也不会被调用。
创建时用作
nil
参数。这意味着当按下按钮时,选择器将被发送到响应链,直到某个对象响应选择器。target:
UIBarButtonItem
当文本字段获得焦点时,它将是第一个响应者,而响应者链的前端可能包含许多 SwiftUI 机制。响应者链上的其中一个 SwiftUI 东西很可能“吞噬”了选择器,从而阻止它到达
UIBarButtonItem
。如果您使用
self
,target:
则选择器将直接发送到,UIBarButtonItem
并且这将按预期工作。也就是说,
UIBarButtonItem
自 iOS 14 以来,s 支持基于闭包的操作。你根本不需要扩展。只需执行以下操作: