UIPickerview as inputview to UITextfield shows weird behavior - shows Keyboard instead of picker selection for working code

I have a working code with more than 20 view controllers. Where I used UIPickerView as input view to UITextField which is populated on a network call. The application was tested locally using .ipa file as well as directly installing through Xcode for couple of iterations. Everything was working fine so far.


But all of a sudden started seeing a weird behavior, Keyboard is showing when I tap on the text field instead of Picker selection.

This issue is happening only for few view controllers where it was working fine earlier. Similar implementation is still working fine for most of the view controllers.

I tried almost all the possibilities including redesigning the page and is working fine on simulator/iPhone when I directly install/run through Xcode.

But when I create an IPA file, still seeing the same issue (i.e. not seeing the picker selection)


I am using Xcode 10.3.


While debugging noticed control is not going to textFieldDidBeginEditing() at all in failed scenarios.

I am completely stuck. Not sure why all of a sudden the working code is broken.


Any direction to fix the issue is much appreciated.

Accepted Reply

No, I don't remember similar issue.


Sometimes, the changes made are not taken immediately into account (a lot of caching by XCode in many places). Just make changes, wait… some time…and suddenly it works.


Computer science is not yet an exact science… 😉


Anyway, great that it works. I advise you to write down the maximum memories of the incident, what you observed exactly, what you tried, what you changed… That may help you in the future.


And make an immediate copy of the working full project.


Don't forget to close the thread now.

Replies

Which iOS version ?


In case, I would try an option clean build folder. There may be some inconsistencies in the project build.


While debugging noticed control is not going to textFieldDidBeginEditing() at all in failed scenarios.

Do you mean textFieldDidBeginEditing is no more called ?


I am completely stuck. Not sure why all of a sudden the working code is broken.

Have you changed somthing in code ? If you revert to an older backup, do you see the problem ?


There have been recently a lot of problems with UITextView, but that is with XCode 11 ; didn't read problem in 10.3.

Thanks Claude for looking into it. updated my answers


Product-> Clean Build Folder didn't help me 😟 so far.


Which iOS version?

IOS version : 12.4.2 iPhone 6

Everything works fine if I install directly on phone/simulator. If I use ipa install, keyboard opens instead of pickerview for few view controllers.


IOS version : 13.1.3 iPhone 8 Plus

If I use ipa install, keyboard opens instead of pickerview for few view controllers.


Do you mean textFieldDidBeginEditing is no more called ?

Yes. textFieldDidBeginEditing is not called for the affected view controllers.


Have you changed something in code ? If you revert to an older backup, do you see the problem?

Re-designed completely for that view controller. Still no luck with ipa install.

Also tried with an old backup(taken on Oct23rd) where ipa install works fine with that.

Also tried with an old backup(taken on Oct23rd) where ipa install works fine with that.


Do you use git ?

If so, you could track all the differences between the 2.

Otherwise, need to do it manually…


Good luck.

Is there any specific file I need to compare related to build settings.


I compared a scene where there is no change but works differently with ipa installation vs direct installation.

The difference could be in the info.list or in the app Targets set up ?


Have a look here, to check how you generate ipa:

h ttps://needone.app/export-ipa-file-from-xcode-and-install-on-your-mobile/


Did you select none for app thinning ?

Could you show the code where you call picker on tap and get keyboard instead.

If possible, could you detail the textField attributes (Text input traits)

The difference could be in the info.list -->

<key>UIStatusBarHidden</key>

<true/> is the only difference I could see


Did you select none for app thinning ?

Yes. None is selected always


Code Snippet

@IBOutlet weak var selectAccountTB: DesignableTextField!

where DesignableTextField extends UITextField


var accountPickerView = UIPickerView()

I have 3 picker views. Listed only 1 here

func textFieldDidBeginEditing(_ textField: UITextField) {
  activeTextField = textField
      self.selectedItem = ""
      if (activeTextField  == selectAccountTB ) {
  let toolBar = UIToolbar.init().addToolBarHandler(selectorDone: #selector(pickerDoneTapped), selectorCancel: #selector(pickerCancelTapped))
  self.activeTextField.isUserInteractionEnabled = true
  self.activeTextField.inputAccessoryView = toolBar
  if (self.activeTextField == self.selectAccountTB ) {
  self.activeTextField.inputView = self.accountPickerView
  }
  }

}
extension QTPViewController : UIPickerViewDelegate , UIPickerViewDataSource {
   
  func numberOfComponents(in pickerView: UIPickerView) -> Int {        return 1    }

  func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
  var count = 1
  if self.activeTextField == self.selectAccountTB {
  count = self.accountList.count
  } else if self.activeTextField == self.selectPayeeBankTB {
  count = self.payeeBankValueList.count
  }
  return count
  }
    
      func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
  var rowValue:String!
  if self.activeTextField == self.selectAccountTB
  {
  rowValue = self.accountList[row]
  } else if self.activeTextField == self.selectPayeeBankTB {
  rowValue = self.payeeBankValueList[row]
  }
  return rowValue
  }
  func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){
  if self.activeTextField == self.selectAccountTB {
  if (!accountList.isEmpty) {
  self.selectedItem = self.accountList[row]
  print("Selected Item " , self.accountList[row])
  }
  }
  }

}

I assume that bchanging UIStatusBarHidden to false does not change anytrhing ?


I am probably missing something in your code. I need to understand before continuing investigation.


    func textFieldDidBeginEditing(_ textField: UITextField) {
        activeTextField = textField
        self.selectedItem = ""
        if (activeTextField  == selectAccountTB ) {
            let toolBar = UIToolbar.init().addToolBarHandler(selectorDone: #selector(pickerDoneTapped), selectorCancel: #selector(pickerCancelTapped))
            self.activeTextField.isUserInteractionEnabled = true
            self.activeTextField.inputAccessoryView = toolBar
            if (self.activeTextField == self.selectAccountTB ) {
                self.activeTextField.inputView = self.accountPickerView
            }
        }
       
    }


I do not find addToolBarHandler anywhere.

Is it a func of your own ?


Why do you repeat the test line 8. Seem to be the same than line 4. Isn't it ?


Thanks for making it clear.

I also have question on extension


extension QTPViewController : UIPickerViewDelegate , UIPickerViewDataSource {
   
    func numberOfComponents(in pickerView: UIPickerView) -> Int {        return 1    }
   
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        var count = 1
        if self.activeTextField == self.selectAccountTB {
            count = self.accountList.count
        } else if self.activeTextField == self.selectPayeeBankTB {
            count = self.payeeBankValueList.count
        }
        return count
    }
   
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        var rowValue:String!
        if self.activeTextField == self.selectAccountTB
        {
            rowValue = self.accountList[row]
        } else if self.activeTextField == self.selectPayeeBankTB {
            rowValue = self.payeeBankValueList[row]
        }
        return rowValue
    }
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){
        if self.activeTextField == self.selectAccountTB {
            if (!accountList.isEmpty) {
                print("Selected Item " , self.accountList[row])
            }
        }
    }
   
}


Line 16: you define rowValue as String!.

What happens if test line 17 and test line 20 fail ?

You should probably define rowValue as String?

do not find addToolBarHandler anywhere.

Is it a func of your own ?


Yes. Used across my application for picker view

extension UIToolbar{
    
    func addToolBarHandler(selectorDone : Selector , selectorCancel : Selector) -> UIToolbar{
        let toolBar = UIToolbar()
        toolBar.barStyle = UIBarStyle.default
        toolBar.isTranslucent = true
        let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.done, target: self, action: selectorDone)
        let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItem.Style.plain, target: self, action: selectorCancel)
        let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
        toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
        toolBar.isUserInteractionEnabled = true
        toolBar.sizeToFit()
        return toolBar
  
  }
}
  

Why do you repeat the test line 8. Seem to be the same than line 4. Isn't it ?

As I mentioned, I have other Pickerviews to check in those lines

func textFieldDidBeginEditing(_ textField: UITextField) {
        activeTextField = textField
        self.selectedItem = ""
        if (activeTextField  == selectAccountTB ||
            activeTextField  == selectPayeeXXXTB  ||
            activeTextField  == selectXXXAccountTB)
        {
            let toolBar = UIToolbar.init().addToolBarHandler(selectorDone: #selector(pickerDoneTapped), selectorCancel: #selector(pickerCancelTapped))
            self.activeTextField.isUserInteractionEnabled = true
            self.activeTextField.inputAccessoryView = toolBar
            if (self.activeTextField == self.selectAccountTB ) {
                self.activeTextField.inputView = self.accountPickerView
            }
            else if(self.activeTextField == self.selectPayeeXXXTB ) {
                self.activeTextField.inputView = self.selectPayeeXXXPickerView
            }
            else if(self.activeTextField == self.selectXXXAccountTB ) {
                self.activeTextField.inputView = self.XXXAccountPickerView
            }
        }
    }

I agree. Didn't come across negative scenarios 🙂

I made it work, but have met a few problems:


I implemented simple actions:

    @objc func pickerDoneTapped() {
        print("Tapped")
    }

    @objc func pickerCancelTapped() {
        print("Cancelled")
    }


They never got called.

That was because the button targets was self, ie the tabBar, not the vc


So, I changed adding a vc parameter:

    func addToolBarHandler(selectorDone : Selector , selectorCancel : Selector, vc: UIViewController ) -> UIToolbar {
        let toolBar = UIToolbar()
        toolBar.barStyle = UIBarStyle.default
        toolBar.isTranslucent = true
        let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.done, target: vc, action: selectorDone)
        let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItem.Style.plain, target: vc, action: selectorCancel)
        let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
        toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
        toolBar.isUserInteractionEnabled = true
        toolBar.sizeToFit()
        return toolBar
  }

called as:

            let toolBar = UIToolbar.init().addToolBarHandler(selectorDone: #selector(pickerDoneTapped), selectorCancel: #selector(pickerCancelTapped), vc: self)

Which worked.


I could not repeat the problem of keyboard being shown instead of picker.

You should search if inputView could be set to nil in some cases.

inputView

var inputView: UIView? { get set }

Discussion

If the value in this property is

nil
, the text field displays the standard system keyboard when it becomes first responder. Assigning a custom view to this property causes that view to be presented instead.


May be you could test self.accountPickerView for nil in func textFieldDidBeginEditing(_ textField: UITextField)


EDITED:

I would try the following: assign the inputView to textField and not to the activeTextField

            textField.inputAccessoryView = toolBar
            if (self.activeTextField == self.selectAccountTB ) {
                textField.inputView = self.accountPickerView
            }

or even to ith IBOutlet directly:

            self.selectAccountTB.inputAccessoryView = toolBar
            if (self.activeTextField == self.selectAccountTB ) {
                self.selectAccountTB.inputView = self.accountPickerView
            }


Or, even better probably:

set the input View and accesssoryView for the 3 textField right in viewDidload, not in textDidBeginEditing.

I could be that it is too late there in some cases and keybaord is already set as inputView.

Thank you Claude31. Just tried your suggestion, The issue still exists with IPA installation whereas direct install works fine.

Arrrghhhh !


Do you confirm it works for some textField ? One of the 3 of this view ?

Others ?

What difference if any between working ones and non working ?


Could you add a test on iputView. Is it nil ? Or what is it ?

As you can't log with .ipa I guess, you could add a temporary label in the view and log there.


Should get something like:

<UIPickerView: 0x7f9fedf0b230; frame = (100 618; 220 140); clipsToBounds = YES; autoresize = W+BM; layer = <CALayer: 0x600002dcc800>>


Another idea: Declare the picker as an IBOutlet (hidden at start) and just unhide in textDidBeginEditing.

Tried setting the input View and accesssoryView for the 3 textFields in viewDidload instead of textDidBeginEditing.

With that picker view displayed on the screen successfully but again textDidBeginEditing() is not invoked where we set the activeTextField.

Due to that pickerDoneTapped() won't work as it's based on activeTextField check and we have some logic to populate screen fields.


A little story behind the actual issue.

I am new to IOS development.

Initially, when I started the project, all the schemes were set to Debug and I was testing with that including ipa installation & everything works fine. Somehow with my last ipa creation, the problem started where pickerview was not showing for almost all the pages.

Due to this issue, I noticed the archive scheme was how set to release.

Whereas simulator/direct phone installation works fine with the same build. ( scheme = Debug)


When I switched back the archive scheme Release to Debug, it started working again.

We need to use a release scheme for the production build. Please correct me if I'm wrong?


I think the issue lies between the archive Debug vs Release scheme.

Is there any difference in the settings which I need to take care?