InputView issues in iOS8

ClockAppDevClockAppDev NLMember ✭✭
edited September 2014 in Xamarin.iOS

I'm currently having issues with custom InputViews set to UITextField in iOS8.

I define my textfields like this:

ListSelection_Input input = new ListSelection_Input ();
listInputField.InputView = input.View;
listInputField.InputAccessoryView = input.AccessoryView;

listInputField is a UITextField defined in a nib.

The ListSelection_Input class is a UIViewController defined like this:

public partial class ListSelectionCell_Input : UIViewController
{
    public UIToolbar AccessoryView { get { return accessoryView; } } //From "ListSelection_Input" nib

    public ListSelectionCell_Input () : base ("ListSelection_Input", null)
    {
    }

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
        //....
    }

    public override void ViewDidAppear (bool animated)
    {
        base.ViewDidAppear();
        //.....
    }
}

Now on iOS 7 this works fine, the InputView shows up and both ViewDidLoad (when the UITextField appears on screen) and ViewDidAppear (when the InputView itself appears on screen) are called.
However on iOS 8 the ViewDidLoad is called the same way, but the ViewDidAppear method is not called when the InputView appears on screen.

There's also the issue that when a hardware keyboard is connected (ex. Bluetooth keyboard or iOS simulator keyboard) only the accessory view appears on iOS. The inputview is nowhere to be seen.

What could be causing these issues and is there any way to solve these?

Posts

  • adamkempadamkemp USInsider, Developer Group Leader mod

    You shouldn't use a view controller like this. That is, you shouldn't use a view controller as if it's just a view. The life cycle methods (like ViewWillAppear and rotation methods) are called by a parent view controller or by the window (if the view controller is the window's RootViewController). If you just use your view controller as a view and add it as a subview of something else then it won't get those notifications because it doesn't have a parent view controller to call them.

    In a case like this you should just just use a plan UIView subclass.

  • GameleonGameleon USMember

    You shouldn't use a view controller like this. That is, you shouldn't use a view controller as if it's just a view.

    Actually, since iOS 5 this is perfectly fine. A subview can be managed by it's own seperate viewcontroller and the ViewDidAppear/ViewDidLoad methods would be called without any issue (it's what iOS controllers like UIPageViewController are based on). The above code should work just fine (and the OP confirms this for iOS 7 and lower).

    Now in iOS 8 you need to explicitly state that the controller managing the input view is a child viewcontroller of your current viewcontroller by adding the following line:

    ListSelection_Input input = new ListSelection_Input ();
    this.AddChildViewController(input); //<--
    listInputField.InputView = input.View;
    listInputField.InputAccessoryView = input.AccessoryView;
    
  • adamkempadamkemp USInsider, Developer Group Leader mod

    iOS 5 added specific APIs for dealing with a hierarchy of view controllers. He is not using those APIs, nor could he use them correctly in this case because he's just giving the view to another view that won't expect it to be a view controller's view. If you don't use the view controller API to establish a parent/child relationship between the view controllers then those methods won't be called.

    Note that ViewDidLoad is a special case because that is triggered by just accessing the View property. If the view hasn't been loaded then the getter for that property calls LoadView and then ViewDidLoad. That will work regardless of how you use the view controller. The other methods, like ViewWillAppear, will only be called if you use the view controller properly, which in this case you are not.

    It's more complicated even than just calling AddChildViewController because there are also other methods you're supposed to call (like DidMoveToParentViewController and WillMoveToParentViewController), and the rules for when to call those are kind of complicated. You might get away without calling those correctly at least, but it's still not right to just call AddChildViewController in this case.

    The keyboard manages the the input accessory view, not your view controller. This is not a proper parent/child relationship because the keyboard is not acting as the parent view controller, and neither is your actual view controller.

    If you're using an accessory view for a keyboard it is simply not proper to use a view controller. It should be just a UIView. I am guessing that you're only using it for the purpose of using a .xib, but there are patterns you can use to load stuff from a .xib for a plain old UIView. You should do that instead. Using this method is brittle at best, which is easily demonstrated by the fact that you're here saying it stopped working.

  • ClockAppDevClockAppDev NLMember ✭✭
    edited September 2014

    Alright then, no UIViewController for the keyboard. I'll just use a UIView.

    But now I'm looking for a solution to another problem.
    Whenever I show the inputview, it needs to show a list of possible inputs (using a UITableView). Those inputs need to be loaded from a webservice and since they can be different each time you focus on the keyboard, they can't be preloaded. Which items shown or hidden in the accessory bar are also dependant on which inputs the webservice returned.

    I don't think subclassing a UIView and doing all the webservice logic in there is a good idea (MVC model and all). I don't really want to put the code in the controller that contains the UITextField because I will be reusing it accross different viewcontrollers. (Which is the reason why I created the inputView viewcontroller in the first place). What would be a good practice in this case?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    I would just make a controller object of some sort that handles the querying (in a background thread) and updates the model, and then have the model support observers. The view would then be an observer to the model so that it can get updates. Don't forget to stop observing when your view leaves the screen.

Sign In or Register to comment.