After updating to Xcode 9.3.1, I've encountered a Thread BAD_ACCESS crash when the keyboard appears by manually making a UITextView becomeFirstResponder.
To be clear, this has previously worked just before updating Xcode to 9.3.1 but now I can't figure out why the crash occurs.
I've made a stripped down example to showcase this. There are no other variables or functions in the UIViewController I've provided.
class ErrorTestsController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupUIElements()
}
private func setupUIElements() {
view.backgroundColor = UIColor.red
view.addSubview(blankTextView)
blankTextView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
blankTextView.widthAnchor.constraint(equalToConstant: 200.0).isActive = true
blankTextView.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
blankTextViewBottomAnchor = blankTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0.0)
blankTextViewBottomAnchor?.isActive = true
// Whether I use safeAreaLayoutGuide or the regular view's bottomAnchor changes nothing
// Add observers to move the blankTextView up when the keyboard appears
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
// add a UITapGestureRecognizer to make the blankTextView becomeFirstResponder and show the keyboard (will also trigger the above NotificationObserver)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showAddTextView)))
}
// Define reference to bottomAnchor of the UITextView called "blankTextView" (using iOS 9.0 + NSLayoutConstraints)
public var blankTextViewBottomAnchor: NSLayoutConstraint?
public var blankTextView: UITextView = {
let textview = UITextView(frame: .zero, textContainer: nil)
textview.translatesAutoresizingMaskIntoConstraints = false
textview.backgroundColor = UIColor.black
return textview
}()
@objc private func showAddTextView() {
blankTextView.becomeFirstResponder()
}
@objc private func keyboardWillShow(notification: NSNotification) {
guard
let keyboardFrame = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue,
let keyboardDuration = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue
else { return }
blankTextViewBottomAnchor?.constant = -keyboardFrame.height
UIView.animate(withDuration: keyboardDuration) {
self.view.layoutIfNeeded()
}
}
@objc private func keyboardWillHide(notification: NSNotification) {
guard
let keyboardDuration = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as AnyObject).doubleValue
else { return }
blankTextViewBottomAnchor?.constant = 0.0
UIView.animate(withDuration: keyboardDuration) {
self.view.layoutIfNeeded()
}
}
}
Here is the crash output:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString isHidden]: unrecognized selector sent to instance 0x1d0224560' *** First throw call stack: (0x181c76d8c 0x180e305ec 0x181c84098 0x181c7c5c8 0x181b6241c 0x18b8351bc 0x18bdb588c 0x18b9c1310 0x18b9c132c 0x18b9c132c 0x18b9c0eec 0x18b9c0850 0x18b9b9934 0x18b84f30c 0x18b9b0330 0x18b8f1b30 0x18b8f1f4c 0x18bac6304 0x100979324 0x100979368 0x18ba26750 0x18bf932a4 0x18bb88e6c 0x18ba257a8 0x18bf84ac4 0x18ba1f540 0x18ba1f078 0x18ba1e8dc 0x18ba1d238 0x18c1fec0c 0x18c2011b8 0x18c201518 0x18c1fa258 0x181c1f404 0x181c1ec2c 0x181c1c79c 0x181b3cda8 0x183b1f020 0x18bb1d78c 0x10089e748 0x1815cdfc0) libc++abi.dylib: terminating with uncaught exception of type NSException
This crash happens whether I use the UITapGestureRecognizer on the self.view or if I manually tap the UITextView itself. Both result in the same crash.
NOTE: On some occasions, I've observed that the actual crash output is different. In this case it displays "[__NSCFString isHidden]:". In other cases, the output was "[__NSArrayI position]:". However, I did not change the code at any time.
But it is always some form of EXC_BAD_ACCESS.
EDIT: FULL STACK TRACE
2018-05-17 12:41:23.714232-0400 TESTAPP[4368:1363924] [Application] Failed to instantiate the default view controller for UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set?
2018-05-17 12:41:25.497939-0400 TESTAPP[4368:1363924] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2018-05-17 12:41:25.500096-0400 TESTAPP[4368:1363924] [MC] Reading from public effective user settings.
2018-05-17 12:41:25.514580-0400 TESTAPP[4368:1363924] -[__NSCFString isHidden]: unrecognized selector sent to instance 0x1d023ec00
2018-05-17 12:41:25.515767-0400 TESTAPP[4368:1363924] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString isHidden]: unrecognized selector sent to instance 0x1d023ec00'
*** First throw call stack:
(0x181c76d8c 0x180e305ec 0x181c84098 0x181c7c5c8 0x181b6241c 0x18b8351bc 0x18bdb588c 0x18b9c1310 0x18b9c132c 0x18b9c132c 0x18b9c0eec 0x18b9c0850 0x18b9b9934 0x18b84f30c 0x18b9b0330 0x18b8f1b30 0x18b8f1f4c 0x18bac6304 0x100fa58cc 0x100fa5910 0x18ba26750 0x18bf932a4 0x18bb88e6c 0x18ba257a8 0x18bf84ac4 0x18ba1f540 0x18ba1f078 0x18ba1e8dc 0x18ba1d238 0x18c1fec0c 0x18c2011b8 0x18c201518 0x18c1fa258 0x181c1f404 0x181c1ec2c 0x181c1c79c 0x181b3cda8 0x183b1f020 0x18bb1d78c 0x100ecad04 0x1815cdfc0)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
Edit 2: This is all done programmatically. Not using storyboards. In my AppDelegate, I've define the UIWindow and its rootView like so:
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
let navigationController = UINavigationController(rootViewController: ErrorTestsController())
window?.rootViewController = navigationController