Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIViewController with inputAccessoryView is not deallocated

I have simple subclass of UIViewController (code below). If I attach inputAccessoryView, my viewcontroller is never deallocated. If I do not set inputAccessoryView in viewDidLoad, dealloc is called as expected.

Am I missing something?

@interface IMTestViewController : UIViewController

@property (nonatomic, strong) UIView *messageInputView;
@property(nonatomic, readwrite, strong) UIView *inputAccessoryView;

@end

@implementation IMTestViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)dealloc
{

}

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.inputAccessoryView = self.messageInputView;
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (UIView *)messageInputView
{
    if (_messageInputView == nil)
    {
        _messageInputView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 45)];
        _messageInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    }
    return _messageInputView;
}
@end

I have ran out of ideas. Thank you.

like image 450
hybridcattt Avatar asked Jul 06 '14 12:07

hybridcattt


2 Answers

Unfortunately for me @rdelmar's answer didn't work. After some time spent trying to solve it I found this article: http://derpturkey.com/uitextfield-docked-like-ios-messenger/

My goal is to have the input accessory view visible even if the keyboard is not, exactly like in all IM apps. I previously subclassed my UIViewController custom class to allow it to become first responder and returned my custom subview as inputAccessoryView. This was preventing the view controller from being dealloced. Now I subclass the controller's view to achieve the same thing as recommended in the link above, everything seems to work fine.

EDIT: after some more testing I can confirm the custom UIView is dealloced just fine.

EDIT 2: only downside is that you can't make the keyboard appear in viewWillAppear, the inputAccessoryView is not already added to the view hierarchy and can't become first responder.

like image 104
dvkch Avatar answered Oct 25 '22 17:10

dvkch


This question is rather old, but I came across it in 2019 when trying to use an inputAccessoryView in iOS 12.

The deallocation problem still exists today and the first solution proposed in the article mentioned in dvkch's answer does not work either. The second solution in the article (involving animations) is just too much work and does not work well when the user dismisses the keyboard interactively via a UIScrollView with scrollView.keyboardDismissMode = .interactive.

The best approach I could come up with is just setting the first responder UITextField or UITextView inputAccessoryView to nil on viewDidDisappear. That gets rid of the memory leak entirely and does not seem to have any side-effects or downsides.

So here's a full Swift 4.2 example:

class MyViewController: UIViewController {
    /// You could also add your text field or text view programmatically, 
    /// but let's say it's coming from a .xib for now...
    @IBOutlet private weak var myTextField: UITextField!

    /// This is required for the inputAccessoryView to work.
    override internal var canBecomeFirstResponder: Bool {
        return true
    }

    /// Here's a nice empty red view that will be used as an
    /// input accessory.
    private lazy var accessoryView: UIView = {
        let accessoryView = UIView()
        accessoryView.backgroundColor = UIColor.red
        accessoryView.frame.size = CGSize(
            width: view.frame.size.width,
            height: 45
        )

        return accessoryView
    } ()

    override var inputAccessoryView: UIView? {
        return accessoryView
    }

    /// This is required to avoid leaking the `inputAccessoryView`
    /// when the keyboard is open and the `UIViewController`
    /// is deallocated.
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        myTextField.inputAccessoryView = nil
    }
}
like image 26
m_katsifarakis Avatar answered Oct 25 '22 18:10

m_katsifarakis