【发布时间】:2021-08-12 06:10:14
【问题描述】:
我最近注意到在同一个视图上两次按钮点击之间存在竞争条件。为了快速重现这个问题,我们可以使用以下代码--
class LandingView: UIView {
private var blackButton: UIButton!
private var redButton: UIButton!
private var testView1: UIView!
private var testView2: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
blackButton = UIButton()
self.addSubview(blackButton)
// sorry I am using Snapkit here for a quick POC. but if you
// just draw any button, it should reproduce the problem as well.
blackButton.snp.makeConstraints{ (maker) in
maker.height.equalTo(30)
maker.top.equalTo(self.safeAreaLayoutGuide.snp.top).offset(20)
maker.left.equalToSuperview().offset(30)
maker.right.equalTo(self.snp.centerX).offset(-30)
}
blackButton.backgroundColor = .black
blackButton.addTarget(self, action: #selector(self.showView1), for: .touchUpInside)
redButton = UIButton()
self.addSubview(redButton)
redButton.snp.makeConstraints{ (maker) in
maker.height.equalTo(30)
maker.top.equalTo(self.safeAreaLayoutGuide.snp.top).offset(20)
maker.left.equalTo(self.snp.centerX).offset(30)
maker.right.equalToSuperview().offset(-30)
}
redButton.backgroundColor = .red
redButton.addTarget(self, action: #selector(self.showView2), for: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented"); }
@objc func showView1() {
self.isUserInteractionEnabled = false
self.redButton.isEnabled = false
self.blackButton.isEnabled = false
NSLog("DEBUG -- before constructing testview1")
testView1 = UIView()
self.addSubview(testView1)
testView1.snp.makeConstraints{ (maker) in
maker.size.equalToSuperview()
maker.center.equalToSuperview()
}
testView1.backgroundColor = .black
let backButton = UIButton()
testView1.addSubview(backButton)
backButton.snp.makeConstraints{ (maker) in
maker.center.equalToSuperview()
maker.width.equalTo(100)
maker.height.equalTo(100)
}
backButton.backgroundColor = .white
backButton.addTarget(self, action: #selector(quitView1), for: .touchUpInside)
testView1.transform = CGAffineTransform(translationX: 0, y: UIScreen.main.bounds.height)
NSLog("DEBUG -- before animating testview1")
UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseInOut], animations: {
self.testView1.transform = CGAffineTransform.identity
}, completion: { _ in
NSLog("DEBUG -- finished animating testview1")
self.isUserInteractionEnabled = true
self.redButton.isEnabled = true
self.blackButton.isEnabled = true
})
}
@objc func quitView1() {
testView1.removeFromSuperview()
testView1 = nil
}
@objc func showView2() {
self.isUserInteractionEnabled = false
self.redButton.isEnabled = false
self.blackButton.isEnabled = false
NSLog("DEBUG -- before constructing testview2")
testView2 = UIView()
self.addSubview(testView2)
testView2.snp.makeConstraints{ (maker) in
maker.size.equalToSuperview()
maker.center.equalToSuperview()
}
testView2.backgroundColor = .red
let backButton = UIButton()
testView2.addSubview(backButton)
backButton.snp.makeConstraints{ (maker) in
maker.center.equalToSuperview()
maker.width.equalTo(100)
maker.height.equalTo(100)
}
backButton.backgroundColor = .white
backButton.addTarget(self, action: #selector(quitView2), for: .touchUpInside)
testView2.transform = CGAffineTransform(translationX: UIScreen.main.bounds.width, y: 0)
NSLog("DEBUG -- before animating testview2")
UIView.animate(withDuration: 0.5, delay: 0, options: [.curveEaseInOut], animations: {
self.testView2.transform = CGAffineTransform.identity
}, completion: { _ in
NSLog("DEBUG -- finished animating testview2")
self.isUserInteractionEnabled = true
self.redButton.isEnabled = true
self.blackButton.isEnabled = true
})
}
@objc func quitView2() {
testView2.removeFromSuperview()
testView2 = nil
}
}
这个实验很简单——你可以在视图控制器中运行这个视图后同时点击这两个按钮,你会看到两个动画同时发生以显示黑色/红色视图。
所以我的期望是,既然我打电话给self.isUserInteractionEnabled = false,我不期望另一个会发生,因为这对我来说是一场意想不到的比赛。但不知何故,self.isUserInteractionEnabled = false 调用在 UIView.animate() 调用的镜头窗口内无效——当然,如果我们没有在完成处理程序中设置self.isUserInteractionEnabled = true,那么整个 UI 就会冻结,这意味着@987654325 @call 大部分时间都在工作。
日志也在一定程度上证明了这一理论。如果我们几乎同时点击按钮,我们会看到如下的日志序列,然后我们会看到view1覆盖view1覆盖testView,而testview1的出现意味着isUserInteractionEnabled = false连同set blackButton/的调用redButton isEnabled = false 不能正常工作,因为另一个按钮仍然对点击有反应。
DEBUG -- before constructing testview2
DEBUG -- before animating testview2
DEBUG -- before constructing testview1
DEBUG -- before animating testview1
DEBUG -- finished animating testview1
DEBUG -- finished animating testview2
有人知道比赛为什么会发生吗?
【问题讨论】:
标签: ios swift swift5 race-condition