iMessage extension crashes while trying to access UITextView in compact mode
Asked Answered
Z

1

0

Below is the entirety of my code in an iMessage app.

class MessagesViewController: MSMessagesAppViewController {

@IBOutlet weak var messageView: UITextView!

fileprivate func setupMessageView() {
    messageView.delegate = self

    messageView.layer.cornerRadius = 10
    messageView.layer.borderColor = UIColor.black.cgColor
    messageView.layer.borderWidth = 5

    messageView.text = "Tap to enter a message"
    messageView.textColor = UIColor(red:0.80, green:0.81, blue:0.82, alpha:1.0)
    messageView.textAlignment = .center

    messageView.font = UIFont.systemFont(ofSize: 20)

    messageView.textContainerInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
}

func initialize() {
    setupMessageView()
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    self.view.endEditing(true)
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(self.initialize), userInfo: nil, repeats: false)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Conversation Handling

override func willBecomeActive(with conversation: MSConversation) {
    // Called when the extension is about to move from the inactive to active state.
    // This will happen when the extension is about to present UI.

    // Use this method to configure the extension and restore previously stored state.
}

override func didResignActive(with conversation: MSConversation) {
    // Called when the extension is about to move from the active to inactive state.
    // This will happen when the user dissmises the extension, changes to a different
    // conversation or quits Messages.

    // Use this method to release shared resources, save user data, invalidate timers,
    // and store enough state information to restore your extension to its current state
    // in case it is terminated later.
}

override func didReceive(_ message: MSMessage, conversation: MSConversation) {
    // Called when a message arrives that was generated by another instance of this
    // extension on a remote device.

    // Use this method to trigger UI updates in response to the message.
}

override func didStartSending(_ message: MSMessage, conversation: MSConversation) {
    // Called when the user taps the send button.
}

override func didCancelSending(_ message: MSMessage, conversation: MSConversation) {
    // Called when the user deletes the message without sending it.

    // Use this to clean up state related to the deleted message.
}

override func willTransition(to presentationStyle: MSMessagesAppPresentationStyle) {
    // Called before the extension transitions to a new presentation style.

    // Use this method to prepare for the change in presentation style.
}

override func didTransition(to presentationStyle: MSMessagesAppPresentationStyle) {
    // Called after the extension transitions to a new presentation style.

    // Use this method to finalize any behaviors associated with the change in presentation style.
}

}

extension MessagesViewController: UITextViewDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
    if textView == messageView {
        requestPresentationStyle(.expanded)
        if textView.textColor == UIColor(red:0.80, green:0.81, blue:0.82, alpha:1.0) {
            textView.text = nil
            textView.textAlignment = .left
            textView.textColor = UIColor.black
            textView.font = UIFont.systemFont(ofSize: 20)
        }
    }
}

func textViewDidEndEditing(_ textView: UITextView) {
    if textView == messageView {
        if textView.text.isEmpty {
            textView.text = "Tap to enter a message"
            textView.textAlignment = .center
            textView.textColor = UIColor(red:0.80, green:0.81, blue:0.82, alpha:1.0)
            textView.font = UIFont.systemFont(ofSize: 20)
        }
    }
}
}

It has a UITextView, and I try enter data. I experience a weird problem while performing this action.

On initial load, if I tap the UITextView, expanded mode is called, but the keyboard doesn't slide up. It needs another tap for the keyboard to slide up.

In the logs, I was able to find, that the textViewDidBeginEditing and textViewDidEndEditing methods are called successively on the first tap. Not sure, why it is happening this way!?

Anyways, what intrigues me more, is what happens now. I can change the mode to compact manually, and back to expanded. If in expanded mode, once I tap, the keyboard slides up. But, if I tap while in compact mode, the app crashes!!

And this happens all the time. On simulator and a real device. I have no clue to explain this behaviour.

No matter how many times I change the mode from compact to expanded and back, I can enter text in expanded mode. But, after the first tap, it never happens again, while in the compact mode.

Does anyone have this issue? Or can you replicate this? Is this a bug wth Apple?

Zaid answered 13/6, 2017 at 9:50 Comment(7)
This sounds similar to a problem I had where I was calling becomeFirstResponder in the same method that called requestPresentationStyle. Mixing those is asking for trouble. It might help if you ensured that the text field did not become first responder until after the transition completes.Dude
@TomHarrington, Okay. So, I haven't used becomeFirstResponder explicitly. Or, do you mean using requestPresentationStyle in textViewDidBeginEditing is the same? Then, how do we recognise that the UITextView has been tapped?Zaid
@TomHarrington, or how to ensure that UITextView doesn't become first responder, until the transition is complete. didTransition(to presentationStyle: ) doesn't work, by the way. ¯_(ツ)_/¯Zaid
In my case we just didn't show the text view. In compact mode there's.a button, and the text view is visible in expanded mode. You could also do it by disabling editing in compact mode but adding a tap gesture recognizer, so that tapping on the text field initiates the change but it doesn't become first responder until the change finishes.Dude
@TomHarrington, You. Are. Simply. Amazing. Period.Zaid
@TomHarrington, thanks a ton. Experience shows!! 🙇Zaid
@TomHarrington, I've gone with your suggestion to disable edit mode. The keyboard still doesn't show up and needs another tap. But, I guess, this is on Apple. In fact the whole issue, is on them.Zaid
H
0

If you need to change the presentation style after the user taps in the textfield, you could add a delay to ensure that there are no clashes with the transitions.

It maybe better to initially prevent the keyboard from showing (You may need to set a bool flag in your VC to switch on off in your textview delegates to enable this), call

  func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
    if  !shouldShowKeyBoard {
        self.requestPresentationStyle(.expanded)
        return false
    }
    return true

Then in the MSMessagesAppViewController delegate enable the keyboard to become first responder `

 override func didTransition(to presentationStyle: MSMessagesAppPresentationStyle) {
    if presentationStyle == .expanded {
       shouldShowKeyBoard = true
       textView.becomeFirstResponder()
    }
}

`

Hopefully that will help out

Harmaning answered 29/4, 2020 at 11:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.