I've spent hours trying to figure out how to create/then get a custom inputView
to work.
I have a grid of TextInputs
(think scrabble board) that when pressed should load a custom inputView
to insert text.
I've created a .xib file containing the UI elements
for the custom inputView
. I was able to create a CustomInputViewController
and have the inputView
appear but never able to get the actual TextInput
to update it's value/text.
Apple documentation has seemed light on how to get this to work and the many tutorials I've have seen have been using Obj-C (which I have been unable to convert over due to small things that seem unable to now be done in swift).
What is the overarching architecture and necessary pieces of code that should be implemented to create a customInputView
for multiple textInputs
(delegate chain, controller, .xibs, views etc)?
You shouldn't go through all that hassle. There's a new class in iOS 8 called: UIAlertController
where you can add TextFields for the user to input data. You can style it as an Alert or an ActionSheet.
Example:
let alertAnswer = UIAlertController(title: "Input your scrabble Answer", message: nil, preferredStyle: .Alert) // or .ActionSheet
Now that you have the controller, add fields to it:
alertAnswer.addTextFieldWithConfigurationHandler { (get) -> Void in
getAnswer.placeholder = "Answer"
getAnswer.keyboardType = .Default
getAnswer.clearsOnBeginEditing = true
getAnswer.borderStyle = .RoundedRect
} // add as many fields as you want with custom tweaks
Add action buttons:
let submitAnswer = UIAlertAction(title: "Submit", style: .Default, handler: nil)
let cancel = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alertAnswer.addAction(submitAnswer)
alertAnswer.addAction(cancel)
Present the controller whenever you want with:
self.presentViewController(alertAnswer, animated: true, completion: nil)
As you see, you have various handlers to pass custom code at any step.
As example, this is how it would look:
Set up a nib file with the appropriate inputView layout and items. In my case I set each button to an action on File Owner of inputViewButtonPressed
.
Set up a storyboard (or nib if you prefer) for a view controller.
Then using the following code, you should get what you're looking for:
class ViewController: UIViewController, UITextFieldDelegate {
var myInputView : UIView!
var activeTextField : UITextField?
override func viewDidLoad() {
super.viewDidLoad()
// load input view from nib
if let objects = NSBundle.mainBundle().loadNibNamed("InputView", owner: self, options: nil) {
myInputView = objects[0] as UIView
}
// Set up all the text fields with us as the delegate and
// using our input view
for view in self.view.subviews {
if let textField = view as? UITextField {
textField.inputView = myInputView
textField.delegate = self
}
}
}
func textFieldDidBeginEditing(textField: UITextField) {
activeTextField = textField
}
func textFieldDidEndEditing(textField: UITextField) {
activeTextField = nil
}
@IBAction func inputViewButtonPressed(button:UIButton) {
// Update the text field with the text from the button pressed
activeTextField?.text = button.titleLabel?.text
// Close the text field
activeTextField?.resignFirstResponder()
}
}
Alternatively, if you're wanting to be more keyboard-like, you can use this function as your action (it uses the new let syntax from Swift 1.2), break it up if you need 1.1 compatibility:
@IBAction func insertButtonText(button:UIButton) {
if let textField = activeTextField, title = button.titleLabel?.text, range = textField.selectedTextRange {
// Update the text field with the text from the button pressed
textField.replaceRange(range, withText: title)
}
}
This uses the UITextInput
protocol to update the text field as appropriate. Handling delete is a little more complicated, but still not too bad:
@IBAction func deleteText(button:UIButton) {
if let textField = activeTextField, range = textField.selectedTextRange {
if range.empty {
// If the selection is empty, delete the character behind the cursor
let start = textField.positionFromPosition(range.start, inDirection: .Left, offset: 1)
let deleteRange = textField.textRangeFromPosition(start, toPosition: range.end)
textField.replaceRange(deleteRange, withText: "")
}
else {
// If the selection isn't empty, delete the chars in the selection
textField.replaceRange(range, withText: "")
}
}
}