From 915ca3051ec03d0709cee8c97aa8c9a2d8ee13db Mon Sep 17 00:00:00 2001 From: Christopher La Pat Date: Thu, 4 Apr 2019 10:49:26 +0100 Subject: [PATCH 1/2] Fix memory leak in which the keyboard was keeping a strong reference to the CardTextField as a result of bugs in overriding first responder functions. --- ...dTextField+CardInfoTextFieldDelegate.swift | 2 +- .../UI/CardTextField+PrefillInformation.swift | 2 +- Pod/Classes/UI/CardTextField.swift | 27 +++---------------- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/Pod/Classes/UI/CardTextField+CardInfoTextFieldDelegate.swift b/Pod/Classes/UI/CardTextField+CardInfoTextFieldDelegate.swift index f1bf0ea..ff59617 100644 --- a/Pod/Classes/UI/CardTextField+CardInfoTextFieldDelegate.swift +++ b/Pod/Classes/UI/CardTextField+CardInfoTextFieldDelegate.swift @@ -44,7 +44,7 @@ extension CardTextField: CardInfoTextFieldDelegate { // Let the next text field become first responder if one of the contained text fields // already is first responder. - if isFirstResponder { + if isChildFirstResponder { nextTextField?.becomeFirstResponder() } diff --git a/Pod/Classes/UI/CardTextField+PrefillInformation.swift b/Pod/Classes/UI/CardTextField+PrefillInformation.swift index 1d106b8..0be5ff9 100644 --- a/Pod/Classes/UI/CardTextField+PrefillInformation.swift +++ b/Pod/Classes/UI/CardTextField+PrefillInformation.swift @@ -43,7 +43,7 @@ extension CardTextField { cvcTextField?.prefill(cvc) } - moveCardNumberOutAnimated(remainFirstResponder: isFirstResponder) + moveCardNumberOutAnimated(remainFirstResponder: isChildFirstResponder) notifyDelegate() } diff --git a/Pod/Classes/UI/CardTextField.swift b/Pod/Classes/UI/CardTextField.swift index 192bd66..c364b3b 100644 --- a/Pod/Classes/UI/CardTextField.swift +++ b/Pod/Classes/UI/CardTextField.swift @@ -191,7 +191,7 @@ open class CardTextField: UITextField, NumberInputTextFieldDelegate { } } - open override var isFirstResponder: Bool { + open var isChildFirstResponder: Bool { // Return true if any of `self`'s subviews is the current first responder. return [numberInputTextField,monthTextField,yearTextField,cvcTextField] .filter({$0.isFirstResponder}) @@ -417,7 +417,7 @@ open class CardTextField: UITextField, NumberInputTextFieldDelegate { /// Function for the swipe gesture recognizer for moving out the card number. @objc private func swipeHideCardNumber() { - moveCardNumberOutAnimated(remainFirstResponder: isFirstResponder) + moveCardNumberOutAnimated(remainFirstResponder: isChildFirstResponder) } /** @@ -506,12 +506,12 @@ open class CardTextField: UITextField, NumberInputTextFieldDelegate { open func numberInputTextFieldDidComplete(_ numberInputTextField: NumberInputTextField) { // Retain the first responder status if currently first responder. - moveCardNumberOutAnimated(remainFirstResponder: isFirstResponder) + moveCardNumberOutAnimated(remainFirstResponder: isChildFirstResponder) notifyDelegate() hideExpiryTextFields = !cardTypeRegister.cardType(for: numberInputTextField.cardNumber).requiresExpiry hideCVCTextField = !cardTypeRegister.cardType(for: numberInputTextField.cardNumber).requiresCVC - if hideExpiryTextFields && hideCVCTextField || !isFirstResponder { + if hideExpiryTextFields && hideCVCTextField || !isChildFirstResponder { return } else if hideExpiryTextFields { cvcTextField.becomeFirstResponder() @@ -603,23 +603,4 @@ open class CardTextField: UITextField, NumberInputTextFieldDelegate { return nil } } - - open override func becomeFirstResponder() -> Bool { - // Return false if any of this text field's subviews is already first responder. - // Otherwise let `numberInputTextField` become the first responder. - if [numberInputTextField,monthTextField,yearTextField,cvcTextField] - .map({return $0.isFirstResponder}) - .reduce(true, {$0 && $1}) { - return false - } - return numberInputTextField.becomeFirstResponder() - } - - open override func resignFirstResponder() -> Bool { - // If any of `self`'s subviews is first responder, resign first responder status. - return [numberInputTextField,monthTextField,yearTextField,cvcTextField] - .filter({$0.isFirstResponder}) - .first? - .resignFirstResponder() ?? true - } } From 2f36d3214d6f6ddd1c2488d498d18aaa244b9dfa Mon Sep 17 00:00:00 2001 From: Christopher La Pat Date: Fri, 5 Apr 2019 10:32:39 +0100 Subject: [PATCH 2/2] Brought back the firstResponder overrides to avoid the situation where a child text field resigns and the CardTextField itself becomes first responder. Note that the logic in becomeFirstResponder is the only way to ensure that the memory leak does not reappear. --- Pod/Classes/UI/CardTextField.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Pod/Classes/UI/CardTextField.swift b/Pod/Classes/UI/CardTextField.swift index c364b3b..3a1688e 100644 --- a/Pod/Classes/UI/CardTextField.swift +++ b/Pod/Classes/UI/CardTextField.swift @@ -603,4 +603,20 @@ open class CardTextField: UITextField, NumberInputTextFieldDelegate { return nil } } + + open override func becomeFirstResponder() -> Bool { + super.becomeFirstResponder() + super.resignFirstResponder() + return false + } + + open override func resignFirstResponder() -> Bool { + // If any of `self`'s subviews is first responder, resign first responder status. + [numberInputTextField,monthTextField,yearTextField,cvcTextField] + .filter({$0.isFirstResponder}) + .first? + .resignFirstResponder() + + return super.resignFirstResponder() + } }